Array.prototype.flatMap()

基线 广泛可用

此功能已得到很好的建立,并且可以在许多设备和浏览器版本上运行。它自以下版本起在所有浏览器中均可用 2020 年 1 月.

flatMap() 方法是 Array 实例的方法,它返回一个新数组,该数组由将给定的回调函数应用于数组的每个元素,然后将结果展平一个级别形成。它与 map() 后跟深度为 1 的 flat()arr.map(...args).flat())相同,但比分别调用这两个方法稍微有效率。

试一试

语法

js
flatMap(callbackFn)
flatMap(callbackFn, thisArg)

参数

callbackFn

要为数组中的每个元素执行的函数。它应该返回一个包含新数组的新元素的数组,或者一个要添加到新数组的单个非数组值。该函数使用以下参数调用

element

数组中正在处理的当前元素。

index

数组中正在处理的当前元素的索引。

array

调用 flatMap() 的数组。

thisArg 可选

在执行 callbackFn 时用作 this 的值。请参阅 迭代方法

返回值

一个新数组,其中每个元素都是回调函数的结果,并展平了一个深度。

描述

flatMap() 方法是一个 迭代方法。请参阅 Array.prototype.map() 以获取有关回调函数的详细说明。flatMap() 方法与 map(callbackFn, thisArg) 后跟 flat(1) 相同——对于每个元素,它都会生成一个包含新元素的数组,并将生成的数组连接在一起以形成一个新数组。阅读 迭代方法 部分以获取有关这些方法如何普遍工作的信息。

flatMap() 方法是 泛型。它只期望 this 值具有 length 属性和整数键属性。但是,如果要展平 callbackFn 返回的值,则它必须是数组。

替代方案

预分配和显式迭代

js
const arr = [1, 2, 3, 4];

arr.flatMap((x) => [x, x * 2]);
// is equivalent to
const n = arr.length;
const acc = new Array(n * 2);
for (let i = 0; i < n; i++) {
  const x = arr[i];
  acc[i * 2] = x;
  acc[i * 2 + 1] = x * 2;
}
// [1, 2, 2, 4, 3, 6, 4, 8]

请注意,在这种特定情况下,flatMap 方法比 for 循环方法慢——这是由于必须垃圾回收的临时数组的创建,以及返回数组不需要频繁调整大小。但是,在需要其灵活性和可读性的情况下,flatMap 仍然可能是正确的解决方案。

示例

map() 和 flatMap()

js
const arr1 = [1, 2, 3, 4];

arr1.map((x) => [x * 2]);
// [[2], [4], [6], [8]]

arr1.flatMap((x) => [x * 2]);
// [2, 4, 6, 8]

// only one level is flattened
arr1.flatMap((x) => [[x * 2]]);
// [[2], [4], [6], [8]]

虽然以上可以使用 map 本身来实现,但这里有一个更好地展示了 flatMap() 用法的示例。

让我们从句子列表中生成一个单词列表。

js
const arr1 = ["it's Sunny in", "", "California"];

arr1.map((x) => x.split(" "));
// [["it's","Sunny","in"],[""],["California"]]

arr1.flatMap((x) => x.split(" "));
// ["it's","Sunny","in", "", "California"]

请注意,输出列表的长度可能与输入列表的长度不同。

对于在 map() 期间添加和删除项目

flatMap 可用作在 map 期间添加和删除项目(修改项目数量)的一种方法。换句话说,它允许您将许多项目映射到许多项目(通过单独处理每个输入项目),而不是始终一对一。从这个意义上说,它的作用类似于 filter 的反面。返回一个 1 元素数组以保留该项目,返回一个多元素数组以添加项目,或返回一个 0 元素数组以删除该项目。

js
// Let's say we want to remove all the negative numbers
// and split the odd numbers into an even number and a 1
const a = [5, 4, -3, 20, 17, -33, -4, 18];
//         |\  \  x   |  | \   x   x   |
//        [4,1, 4,   20, 16, 1,       18]

const result = a.flatMap((n) => {
  if (n < 0) {
    return [];
  }
  return n % 2 === 0 ? [n] : [n - 1, 1];
});
console.log(result); // [4, 1, 4, 20, 16, 1, 18]

使用 callbackFn 的第三个参数

如果您想访问数组中的另一个元素,array 参数非常有用,尤其是在您没有引用该数组的现有变量时。以下示例首先使用 filter() 提取运行站,然后使用 flatMap() 创建一个新数组,其中每个元素包含一个站及其下一个站。在最后一个站,它返回一个空数组以将其排除在最终数组之外。

js
const stations = ["New Haven", "West Haven", "Milford (closed)", "Stratford"];
const line = stations
  .filter((name) => !name.endsWith("(closed)"))
  .flatMap((name, idx, arr) => {
    // Without the arr argument, there's no way to easily access the
    // intermediate array without saving it to a variable.
    if (idx === arr.length - 1) return []; // last station has no next station
    return [`${name} - ${arr[idx + 1]}`];
  });
console.log(line); // ['New Haven - West Haven', 'West Haven - Stratford']

array 参数不是正在构建的数组——无法从回调函数访问正在构建的数组。

在稀疏数组上使用 flatMap()

callbackFn 不会为源数组中的空槽调用,因为 map() 不会调用,而 flat() 会忽略返回数组中的空槽。

js
console.log([1, 2, , 4, 5].flatMap((x) => [x, x * 2])); // [1, 2, 2, 4, 4, 8, 5, 10]
console.log([1, 2, 3, 4].flatMap((x) => [, x * 2])); // [2, 4, 6, 8]

在非数组对象上调用 flatMap()

flatMap() 方法读取 thislength 属性,然后访问其键小于 length 的非负整数的每个属性。如果回调函数的返回值不是数组,则始终将其直接追加到结果数组。

js
const arrayLike = {
  length: 3,
  0: 1,
  1: 2,
  2: 3,
  3: 4, // ignored by flatMap() since length is 3
};
console.log(Array.prototype.flatMap.call(arrayLike, (x) => [x, x * 2]));
// [1, 2, 2, 4, 3, 6]

// Array-like objects returned from the callback won't be flattened
console.log(
  Array.prototype.flatMap.call(arrayLike, (x) => ({
    length: 1,
    0: x,
  })),
);
// [ { '0': 1, length: 1 }, { '0': 2, length: 1 }, { '0': 3, length: 1 } ]

规范

规范
ECMAScript 语言规范
# sec-array.prototype.flatmap

浏览器兼容性

BCD 表格仅在启用 JavaScript 的浏览器中加载。

另请参阅