Array.prototype.flatMap()

Baseline Widely available

This feature is well established and works across many devices and browser versions. It’s been available across browsers since January 2020.

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

¥The flatMap() method of Array instances returns a new array formed by applying a given callback function to each element of the array, and then flattening the result by one level. It is identical to a map() followed by a flat() of depth 1 (arr.map(...args).flat()), but slightly more efficient than calling those two methods separately.

Try it

语法

¥Syntax

js
flatMap(callbackFn)
flatMap(callbackFn, thisArg)

参数

¥Parameters

callbackFn

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

element

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

index

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

array

调用了数组 flatMap()

thisArg Optional

执行 callbackFn 时用作 this 的值。参见 迭代法

返回值

¥Return value

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

¥A new array with each element being the result of the callback function and flattened by a depth of 1.

描述

¥Description

flatMap() 方法是 迭代法 方法。回调函数的详细说明参见 Array.prototype.map()flatMap() 方法与 map(callbackFn, thisArg) 后跟 flat(1) 相同 - 对于每个元素,它生成一个新元素数组,并将结果数组连接在一起形成一个新数组。请阅读 迭代法 部分,了解有关这些方法一般如何工作的更多信息。

¥The flatMap() method is an iterative method. See Array.prototype.map() for a detailed description of the callback function. The flatMap() method is identical to map(callbackFn, thisArg) followed by flat(1) — for each element, it produces an array of new elements, and concatenates the resulting arrays together to form a new array. Read the iterative methods section for more information about how these methods work in general.

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

¥The flatMap() method is generic. It only expects the this value to have a length property and integer-keyed properties. However, the value returned from callbackFn must be an array if it is to be flattened.

选择

¥Alternative

预分配和显式迭代

¥Pre-allocate and explicitly iterate

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 仍然可能是正确的解决方案。

¥Note that in this particular case the flatMap approach is slower than the for-loop approach — due to the creation of temporary arrays that must be garbage collected, as well as the return array not needing to be frequently resized. However, flatMap may still be the correct solution in cases where its flexibility and readability are desired.

示例

¥Examples

map() and 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]]

虽然上述内容可以通过使用地图本身来实现,但这里有一个例子可以更好地展示 flatMap() 的使用。

¥While the above could have been achieved by using map itself, here is an example that better showcases the use of flatMap().

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

¥Let's generate a list of words from a list of sentences.

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"]

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

¥Notice, the output list length can be different from the input list length.

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

¥For adding and removing items during a map()

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

¥flatMap can be used as a way to add and remove items (modify the number of items) during a map. In other words, it allows you to map many items to many items (by handling each input item separately), rather than always one-to-one. In this sense, it works like the opposite of filter. Return a 1-element array to keep the item, a multiple-element array to add items, or a 0-element array to remove the item.

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 的第三个参数

¥Using the third argument of callbackFn

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

¥The array argument is useful if you want to access another element in the array, especially when you don't have an existing variable that refers to the array. The following example first uses filter() to extract operational stations and then uses flatMap() to create a new array where each element contains a station and its next station. On the last station, it returns an empty array to exclude it from the final array.

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 参数不是正在构建的数组 - 无法从回调函数访问正在构建的数组。

¥The array argument is not the array that is being built — there is no way to access the array being built from the callback function.

在稀疏数组上使用 flatMap()

¥Using flatMap() on sparse arrays

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

¥The callbackFn won't be called for empty slots in the source array because map() doesn't, while flat() ignores empty slots in the returned arrays.

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()

¥Calling flatMap() on non-array objects

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

¥The flatMap() method reads the length property of this and then accesses each property whose key is a nonnegative integer less than length. If the return value of the callback function is not an array, it is always directly appended to the result array.

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 } ]

规范

Specification
ECMAScript Language Specification
# sec-array.prototype.flatmap

¥Specifications

浏览器兼容性

BCD tables only load in the browser

¥Browser compatibility

也可以看看