JavaScript 数组扁平化实现详解
一、扁平化概念
数组扁平化是指将一个多维数组转换为一维数组的过程:
arduino
// 多维数组
const arr = [1, [2, [3, [4, 5]], 6], 7];
// 扁平化后
// [1, 2, 3, 4, 5, 6, 7]
二、原生方法(ES2019+)
1. Array.prototype.flat()
arduino
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. 递归实现(基础版)
ini
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. 递归实现(可指定深度)
ini
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实现
scss
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. 使用栈实现(非递归)
ini
function flattenStack(arr) {
const stack = [...arr];
const result = [];
while (stack.length) {
const next = stack.pop();
if (Array.isArray(next)) {
// 将数组元素推入栈中(注意保持顺序)
stack.push(...next.slice().reverse());
} else {
result.push(next);
}
}
return result.reverse();
}
// 优化版本(保持顺序)
function flattenStackOrdered(arr) {
const stack = [];
const result = [];
let current = arr;
let i = 0;
while (current !== undefined) {
if (i < current.length) {
const item = current[i];
i++;
if (Array.isArray(item)) {
// 保存当前状态
stack.push({ current, i });
// 切换到子数组
current = item;
i = 0;
} else {
result.push(item);
}
} else if (stack.length > 0) {
// 恢复上一个状态
const saved = stack.pop();
current = saved.current;
i = saved.i;
} else {
current = undefined;
}
}
return result;
}
5. 使用toString()方法
javascript
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通常最快,栈实现次之