为什么我要放弃javaScript数据结构与算法(第二章)—— 数组
第二章 数组
几乎所有的编程语言都原生支持数组类型,因为数组是最简单的内存数据结构。JavaScript里也有数组类型,虽然它的第一个版本并没有支持数组。本章将深入学习数组数据结构和它的能力。
为什么用数组
需求:保存所在城市每个月的平均温度,可以这么实现
var averageTemp1 = 43.3; var averageTemp2 = 53.2; var averageTemp3 = 14.2; var averageTemp4 = 42.8; var averageTemp5 = 14.8; var averageTemp6 = 78.9;
只是保存前六个月就用了6个变量,显然这种方式不适合保存这类需求。通过数组可以简单地实现我们的需求。
var averageTemp = []; averageTemp[0] = 43.3; averageTemp[1] = 53.2; averageTemp[2] = 14.2; averageTemp[3] = 42.8; averageTemp[4] = 14.8; averageTemp[5] = 78.9;
创建和初始化数组
声明、创建和初始化数组的方式很简单
var temp = new Array(); // 使用 new 关键字,简单声明并初始化一个数组 var temp = new Array(8); // 还可以创建一个指定长度的数组 var temp = new Array(1,2,4,9); // 直接将数组元素作为参数传递给它的构造器
除了用 new
创建数组,还可以通过中括号 []
简单创建数组。
var temp = [1,2,4,9];
访问元素和迭代数组
通过在数组里指定特定位置的元素,可以获取该值或者赋值。而要知道一个数组里所有的元素,可以通过循环遍历数组。
for(var i = 0; i < temp.length; i++){ console.log(temp[i]); // 1,2,4,9 }
案例:斐波那契数列
已知斐波那契数列中的第一个数字是1,第二个数字是2,从第三项开始,每一项都等于前两项之和。求斐波那契数列的前20个数
var fibonacci = []; fibonacci[1] = 1; fibonacci[2] = 2; for(var i =3; i < 20; i++){ fibonacci[i] = fibonacci[i-1] + fibonacci[i-2]; } console.log(fibonacci); // [ 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]
添加元素
var number = [1,2,3]; number[number.length] = 4; number // 1,2,3,4
上述代码可以在数组的最后一位添加元素,但其实还有更加简便的方法:
push
push 能添加任意个元素在数组的末尾
number.push(5); // 5 number.push(6,7); //7 number // [1,2,3,4,5,6,7]
数组使用 push 会返回数组的长度
插入元素到数组首位
首先我们要腾出数组的第一个元素的位置,把所有的元素向右移动一位。我们可以循环数组中的元素,从最后一位+1(长度)开始,将其对应的前一个元素的值赋给它,依次处理,最后把我们想要的值赋给第一个位置(-1)上。
for(var i = number.length; i>=0; i--){ number[i] = number[i-1]; } number[0] = -0;
unshift
或者直接 使用 unshift 方法,可以将数值插入数组的首位:
var number = [1,2,3,4]; number.unshift(-2); // 5 number.unshift(-4,-3); // 7 number // [-4, -3, -2, 1, 2, 3, 4]
数组使用 unshift 会返回数组的长度
删除元素
从数组尾部删除元素
pop
要删除最靠后的元素可以使用 pop 方法,会删除并返回数组的最后一个元素。如果数组已经为空,则 pop() 不改变数组,并返回 undefined 值。
var number = [1,2,3,4]; number.pop(); //4 number // [1,2,3] number.pop() // 3 number // [1]
从数组首部删除元素
如果要移除数组里的第一个元素,可以使用下面的代码
var number = [1,2,3,4]; for(var i = 0;i < number.length; i++){ number[i] = number[i+1]; } number // [2, 3, 4, undefined]
可以看出,我们将数组左移了一位,但数组的长度仍然没有变化,这意味着数组中有一个额外的元素,因为没有定义,所以是 undefined
shift
shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。 数组的长度也会发生变化。如果数组是空的,那么 shift() 方法将不进行任何操作,返回 undefined 值。
小结
修改数组的方法 | 描述 |
---|---|
push | push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度。 |
unshift | unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度。 |
pop | pop() 方法用于删除并返回数组的最后一个元素。 如果数组已经为空,则 pop() 不改变数组,并返回 undefined 值。 |
shift | shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。 如果数组是空的,那么 shift() 方法将不进行任何操作,返回 undefined 值 |
push() 方法和 pop() 方法,能用数组模拟基本的栈的数据结构(先进后出)。
shift()方法和unshift()方法,能用数组模拟基本的队列的数据结构(先进先出 )。
在任意位置添加或者删除元素
已经知道如何删除数组开头和结尾的元素,那么该怎么在数组中的任意位置删除或者添加元素?
splice
splice() 方法向/从数组中添加/删除项目,然后返回被删除的项目。 splice() 方法可删除从 index 处开始的零个或多个元素,并且用参数列表中声明的一个或多个值来替换那些被删除的元素。
语法
arrayObject.splice(index,howmany,item1,.....,itemX)
例子
var number = [1,2,3,4]; number.splice(2,0,4,4,5); // [] number //[1, 2, 4, 4, 5, 3, 4] number.splice(2,5,7); // [4, 4, 5, 3, 4] number //[1, 2, 7]
二维或者多维数组
我们知道如果要记录数天内每个小时的气温,可以使用数组来保存这些数据。那么要保存两天每小时气温的数据的时候可以这样。
var averageTemp1 = [32,53,45,23,46,53]; var averageTemp2 = [98,32,74,34,63,73];
然而这不是最好的方法。可以使用矩阵(二维数组)来存储这些信息。矩阵的行保存每天的数据,列对应小时级别的数据。
var averageTemp = []; averageTemp[0] = [32,53,45,23,46,53]; averageTemp[1] = [98,32,74,34,63,73];
JavaScript只支持一维数组,并不支持矩阵。但是,可以用数组套数组来模拟矩阵或者任一多维数组。
迭代二维数组的元素
如果想看到这矩阵的输出,可以创建一个通用函数,专门输出其中的值:
function printMatrix(x){ for(var i = 0; i < x.length; i++){ for(var j = 0; j< x[i].length; j++){ console.log(x[i][j]); } } } printMatrix(averageTemp);
多维数组
我们也可以用这种方式来处理多维数组。假如我们要创建一个3x3x3的矩阵,每一个格子里包含矩阵的i(行)、j(列)、z(深度)之和:
var matrix3x3x3 = []; for(var i = 0; i < 3; i++){ matrix3x3x3[i] = []; for(var j = 0; j < 3; j++){ matrix3x3x3[i][j] = []; for(var z = 0; z < 3; z++){ matrix3x3x3[i][j][z] = i+j+z; } } }
数据结构中有几个维度都没有关系,都可以用循环遍历每个维度来访问所有格子
for(var i = 0; i < matrix3x3x3.length; i++){ for(var j = 0; j< matrix3x3x3[i].length; j++){ for(var z = 0; z < matrix3x3x3[i][j].length; z++){ console.log(matrix3x3x3[i][j][z]); } } }
如果是一个3x3x3x3的矩阵,代码中就会用四层嵌套的 for 语句,以此类推。
JavaScript 的数组方法参考
在JavaScript里,数组是可以修改的对象。这意味着创建的每一个数组都有一些可用的方法。
下面表格是数组的一些核心方法。
方法名 | 描述 |
---|---|
concat | 连接2个或者更多数组,并返回结果 |
every | 对数组中的每一项运行给定函数,如果该函数对每一项都但返回true,则返回true |
filter | 对数组中度过每一项运行给定函数,返回该函数会返回true的项组成分数组 |
forEach | 对数组中更多每一项运行给定函数,这个方法没有返回值 |
join | 将所有的数组元素连接成一个字符串 |
indexOf | 返回第一个与给定参数相等的数组元素的索引,没有找到则返回-1 |
lastIndexOf | 返回在数组中搜索到的与给定参数相等的元素的索引里最大的值 |
map | 对数组中的每一项运行给定函数,返回每次函数调用结果组成的数组 |
reverse | 颠倒数组中的元素的顺序,原先第一个元素现在变成了最后一个,同样原先的最后一个元素变成了现在的第一个 |
slice | 传入索引值,将数组里对应索引范围内的元素作为新数组返回 |
some | 对数组中每一项运行给定函数,如果任一项返回true,则返回true |
sort | 按照字母的顺序对数组排序,支持传入指定排序方法的函数作为参数 |
toString | 将数组作为字符串返回 |
valueOf | 和 toString 相似,将数组作为字符串返回 |
数组合并
有多个数组,需要合并起来成为一个数组。我们可以迭代各个数组,然后把每个元素加入最终的数组。
JavaScript也有提供相对应的方法 concat()
var a = 0; var b = [1,2,3]; var c = [-3,-2,-1]; var s = c.concat(a,b); s // [-3, -2, -1, 0, 1, 2, 3]
迭代器函数
有时候,我们需要迭代数组中的元素。可以使用循环语句(前面提到的for语句等)。而其实 JavaScript 内置了许多数组可以使用的迭代方法。
对于本节的例子,我们需要函数和数组。假如有一个数组,值是从1到15,如果数组里面的元素可以被2整除(偶数),函数就要返回true,否则就返回false:
var isEven = function(x){ // 如果是 2的倍数,就返回 true return (x % 2 == 0); } var number = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
every
every 会迭代数组中的每个元素,直到返回 false。
number.every(isEven)
在这个例子中,数组number第一个元素是1,不是2的倍数,因此 isEven 函数返回false,然后 every 执行结束。
some
some 方法和 every 相似,不过some方法会迭代数组中的每个元素,直到函数返回true
number.some(isEven)
这个例子中,数组的第二个参数是2,为2的倍数,因此返回true,迭代结束
forEach
如果要迭代整个数组可以用 forEach 方法,和使用 for 循环相同:
number.forEach(function(x){ console.log((x % 2 == 0)); });
map & filter
JavaScript还有两个会返回新数组的遍历方法。第一个是 map:
var myMap = number.map(isEven); myMap // [false, true, false, true, false, true, false, true, false, true, false, true, false, true, false]
从上面代码可以看出,myMap保存了传入 map 方法的 isEven函数运行的结果。这样就可以很容易知道一个元素是否偶数。
还有一个filter方法,它返回的新数组由使函数返回 true 的元素组成:
var evenNumbers = number.filter(isEven); evenNumbers // [2, 4, 6, 8, 10, 12, 14]
reduce
reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。
语法
array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
参数 | 描述 |
---|---|
total | 必需。初始值, 或者计算结束后的返回值。 |
currentValue | 必需。当前元素 |
currentIndex | 可选。当前元素的索引 |
arr | 可选。当前元素所属的数组对象。 |
如果要对一个数组中所有元素进行求和,这就很有用
number.reduce(function(total,currentValue,index){ return total + currentValue; }); // 120
ES6 和数组的新功能
下表是ES6/7新增的数组方法
方法 | 描述 |
---|---|
@@iterator | 返回一个包含数组键值对的迭代器对象,可以通过同步调用得到数组元素的键值对 |
copyWithin | 复制数组中一系列元素到同一数组指定的起始位置 |
entries | 返回包含数组所有键值对的@@iterator |
includes | 如果数组中存在某个元素则返回 true,否则返回false,ES7新增 |
find | 根据回调函数给定的条件从数组中查找元素,如果找到就返回该元素 |
findIndex | 根据回调函数给定的条件从数组中寻找元素,如果找到就返回该元素在数组中的索引 |
fill | 用静态值填充数组 |
from | 根据已有数组创建一个新数组 |
keys | 返回包含数组所有索引的@@iterator |
of | 根据传入的参数创建一个新数组 |
values | 返回包含数组中所有值的@@iterator |
除了这些新的方法,还有一种用 for... of循环来迭代数组的新做法,以及可以从数组实例得到的迭代器对象。
使用 forEach 和箭头函数
箭头函数可以简化使用 forEach迭代数组元素的做法
number.forEach(function(x){ console.log (x % 2 == 0); }) // 等于 number.forEach(x => { console.log(x % 2 == 0); });
使用 for...of 循环迭代
for(let n of number){ console.log(n % 2 == 0); }
使用ES6新的迭代器(@@iterator)
ES6还为 Array 类增加了一个 @@iterator 属性,需要通过 Symbol.iterator来访问。
let iterator = number[Symbol.iterator](); console.log(iterator.next().value); // 1 console.log(iterator.next().value); // 2 console.log(iterator.next().value); // 3 console.log(iterator.next().value); // 4 console.log(iterator.next().value); // 5 console.log(iterator.next().value); // 6
因为number数组中有15个值,所以需要调用15次 iterator.next().value ,数组中所有值都迭代完之后,就会返回 undefined。
-
数组的 entries、keys 和 values 方法
ES6还增加了三种从数组中得到迭代器的方法。
entries 方法返回包含键值对的 @@iterator
let aEntries = number.entries(); // 得到键值对的迭代器 console.log(aEntries.next().value); // [0,1] -- 位置0的值为1 console.log(aEntries.next().value); // [1,2] -- 位置1的值为2 console.log(aEntries.next().value); // [2,3] -- 位置2的值为3
number 数组中都是数字,key是数组中的位置,value是保存在数组中索引的值
使用集合、字段、散列表等数据结构时,能够取出键值对是很有用的。后面会详细讲解。
key 方法返回包含数组索引的 @@iterator
let aKeys = number.entries(); // 得到数组索引的迭代器 console.log(aKeys.next()); // { value: 0, done: false} console.log(aKeys.next()); // { value: 1, done: false} console.log(aKeys.next()); // { value: 2, done: false}
keys方法会返回number数组的索引。一旦没有可以迭代的值,aKeys.next() 就会返回一个value属性为undefined,done属性为 true的对象。如果 done属性为false,就意味着还有可以迭代的值。
使用from方法
Array.from方法根据已有的数组创建一个新数。比如复制number数组:
let number2 = Array.from(number); number2 // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
使用Array.of
Array根据传入的参数创建一个新数组、
let number3 = Array.of(1); let number4 = Array.of(1,2,3,4,5,6); number3 // [1] number4 // [1,2,3,4,5,6] // 复制已有的数组 let numberCopy = Array.of(...number4); numberCopy // [1,2,3,4,5,6]
使用fill方法
fill方法用静态值充填数组。
let numberCopy = Array.of(1,2,3,4,5,6); numberCopy.fill(0); // [0, 0, 0, 0, 0, 0] // 指定开始充填的索引 numberCopy.fill(2,1); // [0, 2, 2, 2, 2, 2] // 指定结束的索引(结束的索引不包括本身) numberCopy.fill(1,3,5); // [0, 2, 2, 1, 1, 2]
创建数组并初始化的时候,fill 方法就非常好用。
let ones = Array(6).fill(1); // 创建了一个长度为6,所有值都是1的数组
使用copyWithin方法
copyWithin方法复制数组中的一系列元素到同一个数组指定的起始位置。
语法:
array.copyWithin(target, start, end)
参数 | 描述 |
---|---|
target | 必需。复制到指定目标索引位置。 |
start | 可选。元素复制的起始位置。 |
end | 可选。停止复制的索引位置 (默认为 array.length)。如果为负值,表示倒数。 |
let copyArray = [1,2,3,4,5,6]; copyArray.copyWithin(0,3); // [4, 5, 6, 4, 5, 6] let copyArray1 = [1,2,3,4,5,6]; copyArray1.copyWithin(1,3,5); // [1, 4, 5, 4, 5, 6]
排序元素
反序输出最开始的长度为15的number数组。
number.reverse(); // [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
尝试使用JavaScript自带的排序函数 sort();
number.sort(); //[1, 10, 11, 12, 13, 14, 15, 2, 3, 4, 5, 6, 7, 8, 9]
跟我们预期的有些不一样,这是因为 sort 方法在对数组进行排序 的时候,把元素默认成字符串进行相互比较。所以我们要自己定义一个比较函数。
number.sort((a,b) =>{ return a -b; }) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
上述代码,如果 b 大于 a,会返回负数,反之就是正数。如果相等的话,就会返回0。下面的写法会清晰一点
function compare(a, b){ if(a < b){ return -1; } if(a > b){ return 1; } return 0; } number.sort(compare);
sort 方法接受 compareFunction作为参数来比较元素。然后sort 会用它来排序数组
自定义排序
我们可以对任何对象类型的数组排序,也可以创建 compareFuntion 来比较元素。例如对象 Person 有名字和属性,我们希望根据年龄排序。
var friends = [ {name: 'John', age: 30}, {name: 'Ana', age: 20}, {name: 'Chris', age: 25} ]; friends.sort((a, b) =>{ return a.age - b.age; }) // {name: "Ana", age: 20} {name: "Chris", age: 25} {name: "John", age: 30}
字符串排序
var names = ['Ana', 'ana', 'John', 'john']; names.sort(); // ["Ana", "John", "ana", "john"]
字符串的比较是根据对应的 ASCⅡ值来比较的。例如 A、J、a、j对应分别是65、74、97、106。
虽然字母表的 a 是排靠前的,但是由于 ASCⅡ值 比较大,所以没有排在首位。如果忽略大小写呢?会是怎么样
names.sort((a, b) =>{ if(a.toLowerCase() < b.toLowerCase()){ return -1; } if(a.toLowerCase() > b.toLowerCase()){ return 1; } return 0; }) // ["Ana", "ana", "John", "john"]
搜索
搜索有两个方法:indexOf方法返回与参数匹配的第一个元素的索引,lastIndexOf返回与参数匹配的最后一个元素的索引。
number.indexOf(10); // 9 number.indexOf(100); // -1 (因为100不存在数组里面) number.puhs(10); number.lastIndexOf(10); // 15 number.lastIndexOf(100) // -1
ES6 find 和 findIndex方法
let number = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; const multipleof13 = (element, index, array) => { return (element % 13 == 0); } number.find(multipleof13); //13 number.findIndex(multipleof13); // 12
find 和 findIndex方法接受一个回调函数,搜索一个满足回调函数条件的值。上面的例子中,我们要从数组里找有个13的倍数。
ES7 使用includes方法
如果数组存在某个元素,includes 方法就会返回 true,否则就返回 false。
number.includes(15) // true number.includes(20) // false number.includes(4,4) // false 第二个参数为开始搜索的索引
输出字符串
toString 和 jion 方法
如果想把数组里所有元素输出位一个字符串,可以使用 toString 方法
number.toString(); // "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15"
还可以用不同的分隔符将元素隔开
number.join('-'); // "1-2-3-4-5-6-7-8-9-10-11-12-13-14-15"
类型数组
JavaScript由于不是强类型的,因此可以存储任意类型的数据,而类型数组则用于存储单一的数据。
语法:
let myArray = new TypedArray(length);
类型数组 | 数据类型 |
---|---|
Int8Array | 8位二进制补码整数 |
Unit8Array | 8位无符号整数 |
Unit8ClampedArray | 8位无符号整数 |
Int16Array | 16位二进制补码整数 |
Unit16Array | 16位无符号整数 |
Int32Array | 32位二进制补码整数 |
Unit32Array | 32位无符号整数 |
Float32Array | 32位IEEE浮点数 |
Float64Array | 64位IEEE浮点数 |
let length = 5; let int16 = new Int16Array(length); let array16 = []; array16.length = length; for(let i = 0;i < length; i++){ int16[i] = i + 1; } console.log(int16); // [1, 2, 3, 4, 5]
使用 webGl API、进行位操作、处理文件和图像时候,类型数组都可以大展拳脚。它用起来和普通数组也毫无二致,本节所学的数组方法和功能都可以用于类型数组。
小结
学习了常见的数据结构:数组。学习了怎么声明和初始化数组,给数组赋值后,以及添加和移除数组元素,学了多维数组和数组的一些操作方法。
下一章,学习栈,一种具有特殊行为的数组。
书籍链接: 学习JavaScript数据结构与算法
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
为什么我要放弃javaScript数据结构与算法(第一章)—— JavaScript简介
数据结构与算法一直是我算比较薄弱的地方,希望通过阅读《javaScript数据结构与算法》可以有所改变,我相信接下来的记录不单单对于我自己有帮助,也可以帮助到一些这方面的小白,接下来让我们一起学习。 第一章 JavaScript简介 众所周知,JavaScript是一门非常强大的编程语言,不仅可以用于前端开发,也适用于后端开发,其中Node.js就是背后的技术。 JavaScript数据结构与算法 那么学习JavaScript数据结构与算法有什么作用呢?首要的作用是数据结构和算法可以高效地解决常见的问题,这影响着代码的质量。其次,在计算机科学里面,算法是基础的概念,是面试的时候的重头戏。 环境搭建 浏览器是最简单的开发环境,下载个Firefox或者Chrome都可以。 下载好浏览器之后可以搭载一个Web服务器,方式有很多种,也都很简单,可以自行百度。下面介绍Node.js. 使用Node.js 搭建服务器 首先到Node.js官网下载和安装Node.js。然后打开终端应用,输入一下命令安装 npm i http-server -g 嫌安装过程慢的,可以安装一下淘宝镜像,就可以用 cnp...
- 下一篇
puppet 自定义facter
自定义fact可以让节点增加更多的标签 在使用puppet作为配置管理工具的同时,facter是一个非常有用的系统盘点工具,这个工具可以通过一些预先设定好变量定位一台主机,比如可以通过变量lsbdistrelease便可以知道当前系统的版本号,通过osfamily便可以知道系统是RedHat还是SLES,还是其它等等。但是这些预先设定好的变量毕竟有限,在整个自动化运维过程中,由于系统应用的多样性,更多需要通过应用的名称、角色的名称进行标示,这样就需要自定义一些fact并赋值到每个节点上去,相当于给节点打上标签。 一、自定义(custom)fact的四种方法 1、定义到facter软件本身的lib库中 这种方法是直接在安装facter的lib库里面直接创建,相当于扩充facter软件的lib库。 可以通过以下方法找到facter包的lib库路径为/usr/lib/ruby/site_ruby/1.8/facter [root@agent1 facter]# rpm -ql facter /usr/bin/facter /usr/lib/ruby/site_ruby/1.8/facter ...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Red5直播服务器,属于Java语言的直播服务器
- CentOS7,CentOS8安装Elasticsearch6.8.6
- Docker安装Oracle12C,快速搭建Oracle学习环境
- CentOS8编译安装MySQL8.0.19
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS6,CentOS7官方镜像安装Oracle11G
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- CentOS8安装Docker,最新的服务器搭配容器使用