JavaScript One Liner 搞定数组初始化
本文主要通过一些示例介绍初始化数组的一些技巧和注意事项,用到的主要方法有:
- Array()
- Array.prototype.fill()
- Array.prototype.map()
- Array.prototype.reduce()
- Array.from()
- Object.keys()
得益于这些优秀的数组方法和 JavaScript 函数式编程特性,我们往往可以通过优雅的单行代码初始化常见的数组。
初始化一维数组
初始化[0 * 5]
使用fill()
const arr = Array(5).fill(0);
// => [0, 0, 0, 0, 0]
使用Array.from()
const arr = Array.from({length: 5}, x => 0)
// => [0, 0, 0, 0, 0]
初始化[0, 1, 2, 3, 4, 5]
这类数值连续的数组初始化过程可归结为 3 步:
- 创建对应长度的数组
- 设法取到数组的索引
- 利用数组索引进行计算获取对应值
Array.from()实现
Array.from()的第二个参数mapFn可直接进行 map 操作
const arr = Array.from(Array(6), (_, idx) => idx);
// => [0, 1, 2, 3, 4, 5]
Array.prototype.map()实现
const arr = Array(6).fill().map((_, idx) => idx);
// => [0, 1, 2, 3, 4, 5]
这里需要注意的是Array(6)创建了数组[empty * 6], 需要使用fill()将其填充为[undefined * 6]。因为map()的回调函数只会在有值的索引上被调用,empty为无值,undefined则为有值(未定义的初始值)。
不使用fill()的结果是完全不同的:
const arr = Array(6).map((_, idx) => idx);
// => [empty * 6]
Object.keys()实现
还可以使用Object.keys()获取数组索引,值得注意的是它获取的索引值为字符串型。
const arr1 = Object.keys(Array(6).fill());
// => ["0", "1", "2", "3", "4", "5"]
const arr2 = Object.keys(Array(6).fill()).map(x => +x);
// => [0, 1, 2, 3, 4, 5]
Object.keys()不会获取无值(即empty)的属性,这点与map()是相通的,同样需要使用fill()将其进行填充。
const arr = Object.keys(Array(6))
// => []
Array.prototype.keys()实现
当然,使用Array.prototype.keys()比Object.keys()更容易实现这种数组:
const arr = [...Array(6).keys()];
// => [0, 1, 2, 3, 4, 5]
需要注意的是:
- 该方法返回一个数值型索引迭代器
- 索引迭代器会包含那些没有对应元素(
empty)的索引
初始化十六进制数字:
const arr = Array.from({length: 16}, (_, idx) => idx.toString(16));
// => ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"]
该例只要在上例的基础上将数字指定为十六进制的字符串形式。
初始化等差数列 [2, 5, 8, 11, 14, 17, 20]
const arr = Array(6).fill().reduce((acc, _, idx) => acc.concat(acc[idx] + 3), [2]);
// => [2, 5, 8, 11, 14, 17, 20]
这里主要利用了 reduce 函数的累计器特性,其累计处理结果即为所求的数组,主要过程为:
- 创建长度为 length - 1的数组,主要为了 reduce 回调函数使用该数组的索引和确定执行的次数
- reduce 设置累计器的初始值为
[2] - reduce 回调函数每执行一次就向累计器追加差值与前一元素之和
初始化等比数列[1, 3, 9, 27, 81]
const arr = Array(4).fill().reduce((acc, _, idx) => acc.concat(acc[idx] * 3), [1])
// => [1, 3, 9, 27, 81]
初始化二维数组
初始化零矩阵
使用fill()创建二维数组也不在话下,如初始化一个零矩阵:
const arr = Array(4).fill(Array(4).fill(0));
// => [
// [ 0, 0, 0, 0 ],
// [ 0, 0, 0, 0 ],
// [ 0, 0, 0, 0 ]
// [ 0, 0, 0, 0 ],
// ]
🔔 然而这是一种错误的初始化方式,此时修改任一元素数值:
arr[1][1] = 1;
// => [
// [ 0, 1, 0, 0 ],
// [ 0, 1, 0, 0 ],
// [ 0, 1, 0, 0 ],
// [ 0, 1, 0, 0 ]
// ]
究其原因是因为fill()填充的是引用值,在初始化arr时,其所有子元素均为同一内存地址的数组。
正确的初始化方法
由于map()回调函数中每次返回的是一个新数组,使用map()配合fill()可以轻松初始化二维数组:
const arr = Array(4).fill().map(_ => Array(4).fill(0));
// => [
// [ 0, 0, 0, 0 ],
// [ 0, 0, 0, 0 ],
// [ 0, 0, 0, 0 ],
// [ 0, 0, 0, 0 ]
// ]
arr[1][1] = 1;
// => [
// [ 0, 0, 0, 0 ],
// [ 0, 1, 0, 0 ],
// [ 0, 0, 0, 0 ],
// [ 0, 0, 0, 0 ]
// ]
初始化单位矩阵
const arr = Array(4).fill().map((_, index) => Array(4).fill(0).map((x, i) => index === i ? 1 : x));
// => [
// [ 1, 0, 0, 0 ],
// [ 0, 1, 0, 0 ],
// [ 0, 0, 1, 0 ],
// [ 0, 0, 0, 1 ]
// ]
初始化全为 1 的上三角矩阵
const arr = Array(4).fill().map((_, index) => Array(4).fill(0).map((x, i) => index <= i ? 1 : x));
// => [
// [ 1, 1, 1, 1 ],
// [ 0, 1, 1, 1 ],
// [ 0, 0, 1, 1 ],
// [ 0, 0, 0, 1 ]
// ]
初始化全为 1 的下三角矩阵
const arr = Array(4).fill().map((_, index) => Array(4).fill(0).map((x, i) => index >= i ? 1 : x));
// => [
// [ 1, 0, 0, 0 ],
// [ 1, 1, 0, 0 ],
// [ 1, 1, 1, 0 ],
// [ 1, 1, 1, 1 ]
// ]
写在最后
想必你能列举出更多的示例,尝试去使用风格优雅、逻辑简单的单行代码来解决复杂的问题吧!