问题抛出:对于
[1, [1,2], [1,2,3]
这样多层嵌套的数组,我们如何将其扁平化为[1, 1, 2, 1, 2, 3]
这样的一维数组呢(数组降维):
1、ES6 的 flat()
先来简单看一下关于它的使用:
const arr = [1, [1,2], [1,2,3]]
arr.flat(Infinity) // [1, 1, 2, 1, 2, 3]
note:
MDN:
flat()
方法会按照一个可指定的深度(传入的参数)递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回
Array.prototype.flat() 特性总结:
- 用于将嵌套的数组扁平化,变成一维的数组。该方法返回一个新数组,对原数据没有影响。
- 不传参数时,默认扁平化一层,可以传入一个整数,表示想要扁平化的层数。
- 传入
<=0
的整数将返回原数组,不扁平化 Infinity
关键字作为参数时,无论多少层嵌套,都会转为一维数组- 如果原数组有空位,
Array.prototype.flat()
会跳过空位。
方法虽好,但是使用这个方法前要先考虑一下兼容性问题哦!!!
我们来实现一个 flat 吧!!!
note:
MDN:
reduce()
方法对数组中的每个元素执行一个由提供的reducer函数(升序执行),将其结果汇总为单个返回值
// 传入两个参数:数组arr 扁平化层数depth(默认是一层)
function flat (arr, depth = 1) {
return depth > 0 ? // 层数大于0才进行扁平化
arr.reduce((acc, cur) => { // acc:reducer 函数的返回值(累计器),cur:当前值(arr 中遍历到的当前元素值)
if (Array.isArray(cur)) { // 当前值是 数组
return [...acc, ...flat(cur, depth - 1)]; // 递归执行 flat 函数,插入返回数组中 累计器的值后面
}
return [...acc, cur]; // 当前值不是数组,直接插入返回数组中 累计器后面
}, [])
: arr // 扁平化层数小于等于0,直接返回原数组 arr
}
2、序列化后正则
const arr = [1, [1,2], [1,2,3]]
const str = `[${JSON.stringify(arr).replace(/(\[|\])/g, '')}]`
JSON.parse(str) // [1, 1, 2, 1, 2, 3]
3、递归
对于树状结构的数据,最直接的处理方式就是递归
const arr = [1, [1,2], [1,2,3]]
function flat(arr) {
let result = []
for (const item of arr) {
item instanceof Array ? result = result.concat(flat(item)) : result.push(item)
}
return result
}
flat(arr) // [1, 1, 2, 1, 2, 3]
4、reduce()递归
const arr = [1, [1,2], [1,2,3]]
function flat(arr) {
return arr.reduce((acc, cur) => {
return acc.concat(cur instanceof Array ? flat(cur) : cur)
}, [])
}
// 或者
function flat(arr) {
return Array.isArray(arr)
? arr.reduce( (acc, cur) => [...acc, ...flattenDeep(cur)] , [])
: [arr]
}
flat(arr) // [1, 1, 2, 1, 2, 3]
5、迭代+展开运算符
// 每次while都会合并一层的元素,这里第一次合并结果为[1, 1, 2, 1, 2, 3, [4,4,4]]
// 然后arr.some判定数组中是否存在数组,因为存在[4,4,4],继续进入第二次循环进行合并
let arr = [1, [1,2], [1,2,3,[4,4,4]]]
while (arr.some(Array.isArray)) {
arr = [].concat(...arr);
}
console.log(arr) // [1, 1, 2, 1, 2, 3, 4, 4, 4]