JavaScript One Liner 搞定数组初始化

795 阅读4分钟

JavaScript One Liner 搞定数组初始化

本文主要通过一些示例介绍初始化数组的一些技巧和注意事项,用到的主要方法有:

得益于这些优秀的数组方法和 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 步:

  1. 创建对应长度的数组
  2. 设法取到数组的索引
  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 ]
// ]

写在最后

想必你能列举出更多的示例,尝试去使用风格优雅、逻辑简单的单行代码来解决复杂的问题吧!