map、flat、flatMap
| 方法 | 核心问题 | 备注 | |
|---|---|---|---|
map() | 每个元素要变成什么? | 每个元素 → 转换 | |
flat() | 已有的嵌套数组要展开几层? | 数组嵌套 → 拆掉 N 层 | |
flatMap() | 每个元素先变换,然后把产生的数组展开一层 | 每个元素 → 转换 → 顺便拆掉一层 | flatMap() 永远只 flat 一层。 |
map
创建一个新数组,新数组由原数组的每个元素都调用一次提供的函数后的返回值组成。
核心问题:每个元素要变成什么?
const arr = [1,2,3];
const newArr = arr.map(item => item * 2);
console.log(newArr); // [2,4,6]语法
arr.map(callback(currentValue, index, array), thisArg)参数
- callback: 生成新数组元素的函数,接收三个参数:
- currentValue: 当前元素的值
- index: 当前元素的索引
- array: 原数组
- thisArg: 可选,执行 callback 函数时使用的 this 值
返回值
- 新数组
flat
创建一个新数组,根据指定深度递归地将所有子数组元素拼接到新的数组中。
直白的说:把嵌套在数组里的子数组“拆掉一层外壳”,把里面的元素提到上一层。
核心问题:已有的嵌套数组要展开几层?
const arr = [1, 2, [3, 4]];
const newArr = arr.flat();
console.log(newArr); // [1,2,3,4]语法:
flat()
flat(depth)参数:
- depth: 可选,指定要提取嵌套数组的结构深度,默认值为 1。
返回值:
一个新的数组,其中包含拼接后的子数组元素。
原来:
[
1,
2,
[3, 4] ← 子数组
]执行 flat() 后,相当于把 [3, 4] 这一层数组外壳拆掉:
[
1,
2,
3,
4
]“递归”的含义是:因为子数组里面还可能继续存在子数组,所以 flat() 可以继续向里面展开。
const arr = [
1,
2,
[3, 4, [5, 6]]
];
const newArr = arr.flat(2);
console.log(newArr); // [1, 2, 3, 4, 5, 6]flat() 默认值为 1,可以传入一个参数,表示展开的层级。
从算法描述上理解:
处理当前数组
↓
发现某个元素还是数组
↓
如果 depth > 0
↓
继续处理这个子数组
↓
如果里面还有数组
↓
继续处理……数组套数组,flat(depth) 决定向里面拆多少层。
flatMap
对数组中的每个元素应用给定的回调函数,然后将结果展开一级,返回一个新数组。
等价于在调用 map() 方法后再调用深度为 1 的 flat() 方法(arr.map(...args).flat()),但比分别调用这两个方法稍微更高效一些。
简单讲就是:先对每个元素做 map(),然后把结果展开一层。
arr.flatMap(fn) 大致等价于 arr.map(fn).flat(1),但效率更高。
核心问题:每个元素先变换,然后把产生的数组展开一层
示例,把每句话拆成单词:
const words = ["hello world", "foo bar"];
const arr1 = words.map(x => x.split(' '))
console.log(arr1);
// [["hello", "world"], ["foo", "bar"]]
// 不是我们想要的 ["hello", "world", "foo", "bar"]
// 需要再执行一次
const arr2 = arr1.flat(1)
console.log(arr2);
// ["hello", "world", "foo", "bar"]
// 实际上是:
const arr3 = words.map(x => x.split(' ')).flat(1)
console.log(arr3);
// ["hello", "world", "foo", "bar"]
// 也就是
const arr4 = words.flatMap(x => x.split(' '))
console.log(arr4);
// ["hello", "world", "foo", "bar"]