Skip to content

使用生成的值初始化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]

这段代码实现了一个生成器函数,只要条件满足,就返回序列中的下一个元素。唯一的主要区别是映射函数只接受两个参数:当前索引和前一个元素。