使用生成的值初始化JavaScript数组
你是否曾经需要使用一系列生成的值来初始化一个数组?或者你可能希望生成一个数组,直到满足某个条件为止?虽然JavaScript的Array.prototype.map()
可能是你的首选解决方案,但在某些情况下,你可能需要使用其他工具来完成任务。
使用map函数初始化数组
在最基本的层面上,你可以使用Array()
构造函数和Array.prototype.map()
来使用一系列值初始化一个数组。由于Array()
构造函数会生成一个空槽的数组,你可能需要先使用Array.prototype.fill()
将其填充为null
值,以避免出现任何奇怪的行为。
const initializeMappedArray = (n, mapFn = (_, i) => i) =>
Array(n).fill(null).map(mapFn);
initializeMappedArray(5); // [0, 1, 2, 3, 4]
initializeMappedArray(5, i => `item ${i + 1}`);
// ['item 1', 'item 2', 'item 3', 'item 4', 'item 5']
在满足条件的情况下初始化数组
前面的代码片段假设要创建的数组的长度是已知的。但是如果你想在满足条件的情况下初始化一个数组呢?例如,你可能希望生成斐波那契数列,直到达到某个数字为止。在这种情况下,你可以使用while
循环来初始化数组。
const initializeArrayWhile = (conditionFn, mapFn) => {
const arr = [];
let i = 0;
let el = mapFn(i, undefined, arr);
while (conditionFn(i, el, arr)) {
arr.push(el);
i++;
el = mapFn(i, el, arr);
}
return arr;
};
initializeArrayWhile(
(i, val) => val < 10,
(i, val, arr) => (i <= 1 ? 1 : val + arr[i - 2])
); // [1, 1, 2, 3, 5, 8]
在这段代码片段中,我们创建了一个空数组arr
,一个索引变量i
和一个元素el
。然后我们使用循环向数组中添加元素,只要conditionFn
函数返回true
,就使用mapFn
函数。
条件函数conditionFn
接受三个参数:当前索引、前一个元素和数组本身。映射函数mapFn
接受三个参数:当前索引、当前元素和数组本身。
[!TIP]
您可以通过使用
do..while
循环来轻松修改此函数的行为,以便在满足条件之前添加元素。
使用迭代函数和种子值初始化数组
Ramda的unfold()
函数提供了另一种独特的初始化数组的方式。它接受一个初始种子值和一个迭代函数来生成序列中的下一个值。迭代函数必须返回一个包含两个元素的数组:序列中的下一个值和下一个种子值。如果迭代函数返回false
,则序列终止。以下是这个概念的原生JavaScript实现:
const unfold = (fn, seed) => {
let result = [],
val = [null, seed];
while ((val = fn(val[1]))) result.push(val[0]);
return result;
};
const f = n => (n > 50 ? false : [-n, n + 10]);
unfold(f, 10); // [-10, -20, -30, -40, -50]
惰性初始化
顺便提一下,其中一些操作可能耗时且占用内存。在许多情况下,您可能不需要一次性初始化整个数组。在这种情况下,您可以使用生成器函数来惰性初始化数组。
由于数组本身不存在于内存中,映射函数无法访问它。相反,您只能将前一个元素作为第二个参数传递给映射函数,从而允许您生成序列中的下一个元素。
const generateArrayWhile = function* (conditionFn, mapFn) {
let i = 0;
let el = mapFn(i, undefined);
while (conditionFn(i, el)) {
yield el;
i++;
el = mapFn(i, el);
}
};
const range5 = generateArrayWhile(i => i < 5, i => i);
[...range5]; // [0, 1, 2, 3, 4]
const doubleUntil50 = generateArrayWhile(
(i, val) => val < 50,
(i, val) => (i < 1 ? 1 : val * 2)
);
[...doubleUntil50]; // [1, 2, 4, 8, 16, 32]
这段代码实现了一个生成器函数,只要条件满足,就返回序列中的下一个元素。唯一的主要区别是映射函数只接受两个参数:当前索引和前一个元素。