FreeCodeCamp JavaScript算法与数据结构 基本数据结构

这篇博客深入介绍了JavaScript中的数据结构,特别是数组和对象的操作。文章讲解了如何使用push、unshift、pop、shift、splice、slice、传播运算符等方法管理数组,以及如何遍历和修改数组。此外,还探讨了对象的属性添加、删除、检查以及遍历,包括如何处理嵌套的对象和数组。文章最后展示了如何使用for循环、for...in语句和Object.keys()方法处理对象。

使用数组存储数据集合

这是一个一维数组(one-dimensional array),它只有一层,或者说在它里面没有包含其它的数组结构。你可以看到它里面包含了布尔值(booleans)、字符串(strings)、数字(numbers)以及一些其他的 JavaScript 语言中合法的数据类型:

let simpleArray = ['one', 2, 'three’, true, false, undefined, null];
console.log(simpleArray.length);
// 输出 7

你可以在上述例子中看到,所有数组都有一个长度(length)属性。你可以简单地使用Array.length方法来访问它。

使用push()和unshift()将项目添加到数组

数组是可变的。在这一挑战中,我们将研究两种可以通过编程方式修改数组的方法:Array.push()和Array.unshift()。

两种方法都将一个或多个元素作为参数,并将这些元素添加到调用该方法的数组中。该push()方法将元素添加到数组的末尾,unshift()并将元素添加到开头。考虑以下:

let twentyThree = 'XXIII';
let romanNumerals = ['XXI', 'XXII'];

romanNumerals.unshift('XIX', 'XX');

romanNumerals将具有价值[‘XIX’, ‘XX’, ‘XXI’, ‘XXII’]。

romanNumerals.push(twentyThree);

romanNumerals将具有价值[‘XIX’, ‘XX’, ‘XXI’, ‘XXII’, ‘XXIII’]。注意,我们还可以传递变量,这使我们在动态修改数组数据时具有更大的灵活性。

使用pop()和shift()从数组中删除项目

pop() 删除从阵列的端部的元件,而shift()将删除开头的元素。

使用splice()删除项目

splice()允许我们做到这一点:从数组中的任何位置删除任意数量的连续元素。

splice()最多可以包含3个参数,但现在,我们仅关注前两个参数。的前两个参数splice()是整数,它们表示splice()被调用数组的索引或位置。请记住,数组是零索引的,因此要使用表示数组的第一个元素0。splice()的第一个参数表示要从中开始删除元素的数组上的索引,而第二个参数表示要删除的元素数。例如:

let array = ['today', 'was', 'not', 'so', 'great'];


array.splice(2, 2);

在这里,我们从第三个元素(索引2)开始删除2个元素。array将具有价值[‘today’, ‘was’, ‘great’]。

使用splice()添加项目

您可以使用由一个或多个元素组成的第三个参数添加到数组中。这对于将一个元素或一组元素快速切换出另一个元素非常有用。

const numbers = [10, 11, 12, 12, 15];
const startIndex = 3;
const amountToDelete = 1;

numbers.splice(startIndex, amountToDelete, 13, 14);
console.log(numbers);

的第二个条目12被删除,我们在相同的索引处添加13和14。该numbers数组现在为[ 10, 11, 12, 13, 14, 15 ]。

在这里,我们从数字数组开始。然后,将以下内容传递给splice():从该索引处开始删除元素的索引(3),要删除的元素数(1)和其余参数(13、14)。请注意,后面可以有任意多个元素(用逗号分隔)amountToDelete,每个元素都将被插入。

使用slice()复制数组项

我们将介绍的下一个方法是slice()。无需修改数组,而是将给定数量的元素slice()复制或提取到新数组中,而无需修改即可调用该数组。slice()仅使用2个参数-第一个是开始提取的索引,第二个是停止提取的索引(提取将一直进行,直到但不包括该索引处的元素)。考虑一下:

let weatherConditions = ['rain', 'snow', 'sleet', 'hail', 'clear'];

let todaysWeather = weatherConditions.slice(1, 3);

todaysWeather会拥有价值[‘snow’, ‘sleet’],而weatherConditions仍然会有[‘rain’, ‘snow’, ‘sleet’, ‘hail’, ‘clear’]。

实际上,我们通过从现有数组中提取元素来创建了一个新数组。

使用Spread运算符复制阵列

尽管slice()可以让我们在复制数组的哪些元素方面有选择余地,但还有其他一些有用的任务,ES6的新扩展运算符使我们可以使用简单且易读的语法轻松地依次复制数组的所有元素。传播语法看起来像这样:…

在实践中,我们可以使用spread运算符来复制数组,如下所示:

let thisArray = [true, true, undefined, false, null];
let thatArray = [...thisArray];
thatArray等于[true, true, undefined, false, null]

thisArray保持不变,并thatArray包含与相同的元素thisArray。

将数组与传播运算符组合

散布运算符的另一个巨大优势是能够组合数组,或在任何索引处将一个数组的所有元素插入另一个数组的能力。使用更传统的语法,我们可以连接数组,但这仅允许我们在一个数组的结尾和另一个数组的开头合并数组。传播语法使以下操作极为简单:

let thisArray = ['sage', 'rosemary', 'parsley', 'thyme'];

let thatArray = ['basil', 'cilantro', ...thisArray, 'coriander'];

thatArray将具有价值[‘basil’, ‘cilantro’, ‘sage’, ‘rosemary’, ‘parsley’, ‘thyme’, ‘coriander’]。

使用indexOf()检查元素是否存在

由于阵列是可以改变的,或突变的,在任何时候,没有关于在数据的特定部分将是一个给定的阵列上的保证,或者,如果该元件甚至仍然存在。幸运的是,JavaScript为我们提供了另一种内置方法,indexOf()它使我们能够快速轻松地检查数组中元素的存在。indexOf()将元素作为参数,并在调用时返回该元素的位置或索引,或者-1该元素在数组中不存在。

例如:

let fruits = ['apples', 'pears', 'oranges', 'peaches', 'pears'];

fruits.indexOf('dates');
fruits.indexOf('oranges');
fruits.indexOf('pears');

indexOf(‘dates’)返回-1,indexOf(‘oranges’)返回2和indexOf(‘pears’)返回1(每个元素存在的第一个索引)。

indexOf()对于快速检查数组中元素的存在非常有用。我们定义了一个函数,该函数quickCheck采用一个数组和一个元素作为参数。使用修改函数,indexOf()以便true如果传递的元素存在于数组中(false如果不存在)则返回该函数。

function quickCheck(arr, elem) {
  // Only change code below this line
if(arr.indexOf(elem)>=0){
  return true; 
}
else return false;
  // Only change code above this line
}

console.log(quickCheck(['squash', 'onions', 'shallots'], 'mushrooms'));

使用For循环遍历数组的所有项目

有时,在使用数组时,能够遍历每个项目以找到我们可能需要的一个或多个元素,或者根据哪些数据项满足一组特定条件来操纵数组,这一点非常方便。JavaScript的提供了多种内置的方法,每个叠代阵列稍微不同的方式来实现不同的效果(如every(),forEach(),map(),等),但该技术是最灵活,为我们提供了控制量最大的是一个简单的for循环。

考虑以下:

function greaterThanTen(arr) {
  let newArr = [];
  for (let i = 0; i < arr.length; i++) {
    if (arr[i] > 10) {
      newArr.push(arr[i]);
    }
  }
  return newArr;
}

greaterThanTen([2, 12, 8, 14, 80, 0, 1]);
使用for循环,此函数遍历并访问数组的每个元素,并将其置于我们创建的简单测试中。这样,我们就可以轻松地以编程方式确定哪些数据项大于10,并返回[12, 14, 80]包含这些项的新数组。

我们定义了一个函数,filteredArray该函数采用arr嵌套数组和elem作为参数,并返回一个新数组。elem表示一个元素,该元素可能会或可能不会出现在嵌套在其中的一个或多个数组上arr。使用for循环修改函数,以返回传递的数组的过滤后的版本,以使嵌套在arrcontains中的任何数组elem都已删除。

第一种

function filteredArray(arr, elem) {
  let newArr = [];
  // Only change code below this line
for(let i=0;i<arr.length;i++)
{
  let a=[];
  let flag=0;
    for(let j=0;j<arr[i].length;j++)
  {
    
    if(elem==arr[i][j])
    {
      flag=1;
      break;
    } 
    a.push(arr[i][j]);
  }
  if(flag==0)
  newArr.push(a);
}
  // Only change code above this line
  return newArr;
}

console.log(filteredArray([[3, 2, 3], [1, 6, 3], [3, 13, 26], [19, 3, 9]], 3));

第二种

function filteredArray(arr, elem) {
  let newArr = [];
  // Only change code below this line
for(let i=0;i<arr.length;i++)
{
  if(arr[i].indexOf(elem)==-1){
    newArr.push(arr[i]);
  }
}
  // Only change code above this line
  return newArr;
}

console.log(filteredArray([[3, 2, 3], [1, 6, 3], [3, 13, 26], [19, 3, 9]], 3));

创建复杂的多维数组

当将数组视为数据结构时,最强大的功能之一就是数组可以包含甚至完全由其他数组组成。我们已经看到了在以前的挑战中包含数组的数组,但是相当简单。但是,数组可以包含无限深度的数组,而该数组可以包含其他数组,每个数组都有自己的任意深度级别,依此类推。这样,数组可以很快变成非常复杂的数据结构,称为多维数组或嵌套数组。考虑以下示例:

let nestedArray = [
  ['deep'],
  [
    ['deeper'], ['deeper'] 
  ],
  [
    [
      ['deepest'], ['deepest']
    ],
    [
      [
        ['deepest-est?']
      ]
    ]
  ]
];

该deep数组嵌套了2层深度。该deeper阵列是3级深。该deepest阵列是4个级别,而deepest-est?为5。

尽管此示例看起来有些复杂,但是在处理大量数据时,这种复杂程度并不是闻所未闻的,甚至是不寻常的。但是,我们仍然可以非常容易地使用方括号表示法访问这种复杂的数组的最深层:

console.log(nestedArray[2][1][0][0][0]);

这将记录字符串deepest-est?。现在我们知道了该数据的位置,如果需要,我们可以将其重置:

nestedArray[2][1][0][0][0] = 'deeper still';

console.log(nestedArray[2][1][0][0][0]);

现在它记录了deeper still。

将键值对添加到JavaScript对象

从最基本的角度讲,对象只是键值对的集合。换句话说,它们是映射到称为属性(键)的唯一标识符的数据(值)。看一个例子:

const tekkenCharacter = {
  player: 'Hwoarang',
  fightingStyle: 'Tae Kwon Doe',
  human: true
};

上面的代码定义了一个名为的Tekken视频游戏角色对象tekkenCharacter。它具有三个属性,每个属性都映射到一个特定值。如果要添加其他属性,例如“ origin”,可以通过分配origin给对象来完成:

tekkenCharacter.origin = 'South Korea';

这使用点表示法。如果您要观察该tekkenCharacter对象,则该对象现在将包含该origin属性。霍瓦朗也有明显的橙色头发。您可以通过执行以下操作以方括号符号添加此属性:

tekkenCharacter['hair color'] = 'dyed orange';

如果属性中有空格,或者要使用变量来命名属性,则必须使用括号表示法。在上述情况下,该属性用引号括起来以将其表示为字符串,并将完全按所示添加。如果不带引号,它将被视为一个变量,并且属性名称将是该变量的任何值。这是一个带有变量的示例:

const eyes = 'eye color';

tekkenCharacter[eyes] = 'brown';

添加所有示例后,该对象将如下所示:

{
  player: 'Hwoarang',
  fightingStyle: 'Tae Kwon Doe',
  human: true,
  origin: 'South Korea',
  'hair color': 'dyed orange',
  'eye color': 'brown'
};

一个foods对象已经有三个项目创建的。使用您选择的语法,再添加三个条目:bananas值为13,grapes值为35以及strawberries的值27。

let foods = {
  apples: 25,
  oranges: 32,
  plums: 28
};

// Only change code below this line
foods.bananas=13;
foods['grapes']=35;
const a='strawberries';
foods[a]=27;
// Only change code above this line

console.log(foods);

修改嵌套在对象中的对象

let nestedObject = {
  id: 28802695164,
  date: 'December 31, 2016',
  data: {
    totalUsers: 99,
    online: 80,
    onlineStatus: {
      active: 67,
      away: 13,
      busy: 8
    }
  }
};

nestedObject具有三个属性:(id值是数字),date(值是字符串)和data(值是具有嵌套结构的对象)。尽管结构可能很快变得复杂,但是我们仍然可以使用相同的符号来访问所需的信息。要将值分配给嵌套对象10的busy属性onlineStatus,我们使用点表示法引用该属性:

nestedObject.data.onlineStatus.busy = 10;

使用括号符号访问属性名称

注意:未知变量不能使用“.”访问

let selectedFood = getCurrentFood(scannedItem);
let inventory = foods[selectedFood];

此代码将评估存储在selectedFood变量中的值,并在foods对象中(undefined如果不存在)返回该键的值。括号表示法非常有用,因为有时对象属性在运行时之前是未知的,或者我们需要以更动态的方式访问它们。

使用delete关键字删除对象属性

让我们foods最后一次回顾我们的对象示例。如果要删除apples密钥,可以使用如下delete关键字将其删除:

delete foods.apples;

检查对象是否具有属性

JavaScript为我们提供了两种不同的方法。一种使用该hasOwnProperty()方法,另一种使用in关键字。如果我们有一个对象users,其属性为Alan,则可以通过以下两种方式检查其是否存在:

users.hasOwnProperty('Alan');
'Alan' in users;

这两个都会回来true。

我们创建了一个对象,users其中包含一些用户和一个函数isEveryoneHere,我们将该users对象作为参数传递给该对象。写完这个功能,所以它返回true只有当users对象包含所有四个名字,Alan,Jeff,Sarah,和Ryan,钥匙,和false其他。

let users = {
  Alan: {
    age: 27,
    online: true
  },
  Jeff: {
    age: 32,
    online: true
  },
  Sarah: {
    age: 48,
    online: true
  },
  Ryan: {
    age: 19,
    online: true
  }
};

function isEveryoneHere(obj) {
  // Only change code below this line

  return 'Alan' in users&&'Jeff' in users&&'Sarah' in users&&'Ryan' in users;
  // Only change code above this line
}

console.log(isEveryoneHere(users));

使用for … in语句遍历对象的键

有时候你需要遍历一个对象中的所有键。这需要 JavaScript 中的一个特殊语法:for…in 语句。以遍历 users 对象的键为例:

for (let user in users) {
  console.log(user);
};
 
// 输出:
Alan
Jeff
Sarah
Ryan

在这个语句中,我们定义了一个user变量,你可以看到,这个变量在 for…in 语句对对象的每一个键的遍历中都会被重置。
注意:
跟数组不同,对象中的键是无序的,因此一个对象中某个键的位置,或者说它出现的相对顺序,在引用或访问该键时是不确定的。

我们定义了一个countOnline接受一个参数(用户对象)的函数。在此函数内使用for … in语句循环遍历传递给函数的users对象,并返回其online属性设置为的用户数true。countOnline下面显示了可以传递给的用户对象的示例。每个用户将具有一个online带有atrue或falsevalue的属性。

{
  Alan: {
    online: false
  },
  Jeff: {
    online: true
  },
  Sarah: {
    online: false
  }
}
function countOnline(usersObj) {
  // Only change code below this line
  let n=0;
for(let a in usersObj){
  if (usersObj[a].online==true){
    n++;
  }
}
return n;
  // Only change code above this line
}

console.log(countOnline({ Alan: { online: false }, Jeff: { online: true }, Sarah: { online: false } }));

使用Object.keys()生成所有对象键的数组

我们还可以生成一个数组,其中包含使用该Object.keys()方法存储在对象中并传入对象作为参数的所有键。这将返回一个带有字符串的数组,该字符串表示对象中的每个属性。同样,数组中的条目将没有特定的顺序。

完成getArrayOfUsers函数的编写,使其返回一个数组,该数组包含作为参数接收的对象中的所有属性。

let users = {
  Alan: {
    age: 27,
    online: false
  },
  Jeff: {
    age: 32,
    online: true
  },
  Sarah: {
    age: 48,
    online: false
  },
  Ryan: {
    age: 19,
    online: true
  }
};

function getArrayOfUsers(obj) {
  // Only change code below this line
return Object.keys(obj);
  // Only change code above this line
}

console.log(getArrayOfUsers(users));

修改存储在对象中的数组

看一下我们在代码编辑器中提供的对象。该user对象包含三个键。该data键包含五个键,其中一个包含的数组friends。由此,您可以看到对象作为数据结构的灵活性。我们已经开始编写函数addFriend。完成编写,使其接受一个user对象,并将friend参数名称添加到存储的数组中user.data.friends并返回该数组。

let user = {
  name: 'Kenneth',
  age: 28,
  data: {
    username: 'kennethCodesAllDay',
    joinDate: 'March 26, 2016',
    organization: 'freeCodeCamp',
    friends: [
      'Sam',
      'Kira',
      'Tomo'
    ],
    location: {
      city: 'San Francisco',
      state: 'CA',
      country: 'USA'
    }
  }
};

function addFriend(userObj, friend) {
  // Only change code below this line
userObj.data.friends.push(friend);
return userObj.data.friends;
  // Only change code above this line
}

console.log(addFriend(user, 'Pete'));
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值