JavaScript 数组扁平化实现详解
一、扁平化概念
数组扁平化是指将一个多维数组转换为一维数组的过程:
// 多维数组
const arr = [1, [2, [3, [4, 5]], 6], 7];
// 扁平化后
// [1, 2, 3, 4, 5, 6, 7]
二、原生方法(ES2019+)
1. Array.prototype.flat()
const arr = [1, [2, [3, [4, 5]], 6], 7];
// 默认只展开一层
console.log(arr.flat()); // [1, 2, [3, [4, 5]], 6, 7]
// 指定展开深度
console.log(arr.flat(2)); // [1, 2, 3, [4, 5], 6, 7]
// 完全展开(Infinity表示无限深度)
console.log(arr.flat(Infinity)); // [1, 2, 3, 4, 5, 6, 7]
// 移除空位
console.log([1, 2, , 3, 4].flat()); // [1, 2, 3, 4]
三、手动实现方法
1. 递归实现(基础版)
function flatten(arr) {
let result = [];
for (let i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i])) {
result = result.concat(flatten(arr[i]));
} else {
result.push(arr[i]);
}
}
return result;
}
// 使用示例
const arr = [1, [2, [3, [4, 5]], 6], 7];
console.log(flatten(arr)); // [1, 2, 3, 4, 5, 6, 7]
2. 递归实现(可指定深度)
function flattenDepth(arr, depth = 1) {
if (depth === 0) return arr.slice(); // 深度为0,直接返回副本
let result = [];
for (let i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i]) && depth > 0) {
result = result.concat(flattenDepth(arr[i], depth - 1));
} else {
result.push(arr[i]);
}
}
return result;
}
// 使用示例
const arr = [1, [2, [3, [4, 5]], 6], 7];
console.log(flattenDepth(arr, 1)); // [1, 2, [3, [4, 5]], 6, 7]
console.log(flattenDepth(arr, 2)); // [1, 2, 3, [4, 5], 6, 7]
console.log(flattenDepth(arr, Infinity)); // [1, 2, 3, 4, 5, 6, 7]
3. 使用reduce实现
function flattenReduce(arr) {
return arr.reduce((result, current) => {
return result.concat(
Array.isArray(current) ? flattenReduce(current) : current
);
}, []);
}
// 带深度的reduce版本
function flattenReduceDepth(arr, depth = 1) {
return depth > 0
? arr.reduce((acc, val) =>
acc.concat(Array.isArray(val)
? flattenReduceDepth(val, depth - 1)
: val
), [])
: arr.slice();
}
4. 使用栈实现(非递归)
function flattenArray(arr) {
const stack = [...arr]; // 将数组转为栈,初始放入所有元素
const result = [];
while (stack.length) {
const item = stack.pop(); // 弹出栈顶元素
if (Array.isArray(item)) {
// 如果是数组,将其元素按原顺序推入栈中
for (let i = item.length - 1; i >= 0; i--) {
stack.push(item[i]);
}
} else {
result.push(item);
}
}
return result.reverse(); // 由于栈是后进先出,需要反转恢复原顺序
}
// 使用示例
const nestedArr = [1, [2, [3, 4], 5], 6];
console.log(flattenArray(nestedArr)); // [1, 2, 3, 4, 5, 6]
5. 使用toString()方法
function flattenToString(arr) {
return arr.toString()
.split(',')
.map(item => {
// 转换回适当的数据类型
const num = Number(item);
return isNaN(num) ? item : num;
});
}
// 注意:这种方法会将所有元素转为字符串再解析
// 只适用于纯数字数组或可转换为字符串的元素
const arr = [1, [2, [3, [4, 5]], 6], 7];
console.log(flattenToString(arr)); // [1, 2, 3, 4, 5, 6, 7]
// 局限性示例
const mixedArr = [1, [2, ['a', ['b', 'c']]], 3];
console.log(flattenToString(mixedArr)); // [1, 2, 'a', 'b', 'c', 3]
总结
推荐方法选择
- 现代项目(支持ES2019+) :直接使用
arr.flat(Infinity) - 需要深度控制:使用递归版本
flattenDepth - 大数组或性能敏感:使用栈实现的非递归版本
- 需要处理循环引用:使用
flattenSafe或完整版 - 简单场景:使用reduce或递归基础版
注意事项
- 方法选择要考虑浏览器兼容性
- 递归方法可能导致栈溢出(深度过大)
- 字符串转换方法有类型丢失问题
- 注意处理稀疏数组和循环引用
- 性能测试显示原生
flat通常最快,栈实现次之