数组扁平化(Flatten)是指将多层嵌套的数组转换为一维数组。以下是基于不同场景和需求的多种手写实现方法,结合了递归、迭代、函数式编程等思路:
一、基础递归实现
function flatten(arr) {
const result = [];
for (const item of arr) {
if (Array.isArray(item)) {
result.push(...flatten(item)); // 递归展开子数组
} else {
result.push(item);
}
}
return result;
}
// 示例
const arr = [1, [2, [3, [4, 5]]], 6];
console.log(flatten(arr)); // [1, 2, 3, 4, 5, 6]
特点:简单直观,适用于任意深度嵌套,但可能存在性能问题(深层递归栈溢出风险)
二、使用 reduce
方法
利用 reduce
的累加器特性,结合递归实现扁平化。
const flatten = (arr) =>
arr.reduce((acc, val) =>
Array.isArray(val) ? acc.concat(flatten(val)) : acc.concat(val), []);
// 示例
console.log(flatten([[1,2], [3,4]])); // [1,2,3,4]
特点:代码简洁,函数式编程风格,但需注意 concat
的性能消耗[3,6]。
三、扩展运算符 + concat
通过扩展运算符展开数组,结合 concat
实现单层扁平化。
const flatten = (arr) => [].concat(...arr);
// 示例
console.log(flatten([1, [2,3], 4])); // [1,2,3,4]
特点:代码极简,但无法处理多层嵌套,仅展开一层[7]。
四、控制展开深度的实现
通过参数 depth
控制扁平化层级,支持任意深度。
function flattenDeep(arr, depth = 1) {
if (depth === 0) return arr;
return arr.reduce((acc, val) =>
Array.isArray(val)
? acc.concat(flattenDeep(val, depth - 1))
: acc.concat(val), []);
}
// 示例
const nestedArr = [1, [2, [3, [4]]], 5];
console.log(flattenDeep(nestedArr, Infinity)); // [1,2,3,4,5]
特点:灵活控制深度,Infinity
表示完全展开3,8。
五、迭代器遍历实现
通过迭代器逐层遍历元素,避免递归栈溢出。
function flatten(arr) {
const result = [];
const stack = [...arr]; // 使用栈模拟递归
while (stack.length) {
const item = stack.pop();
if (Array.isArray(item)) {
stack.push(...item.reverse()); // 反转顺序保持原顺序
} else {
result.push(item);
}
}
return result.reverse(); // 恢复原始顺序
}
// 示例
console.log(flatten([1, [2, [3,4]]])); // [1,2,3,4]
特点:非递归实现,适合处理深层嵌套数组[6,8]。
六、特殊场景优化方案
1. 处理空位
若原数组存在空位(如 [1,,3]
),需保留或过滤空位:
const arr = [1,, [3]];
const flattenWithHoles = arr.flat(Infinity); // [1,3]
2. 字符串转数字
若数组元素包含字符串数字,需转换类型:
const strArr = ['1', ['2', '3']];
const numArr = flatten(strArr).map(Number); // [1,2,3]
七、方法对比
方法 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
递归实现 | 简单直观,支持任意深度 | 深层嵌套可能导致栈溢出 | 通用场景 |
reduce + 递归 | 函数式风格,代码简洁 | 性能略低 | 需要累加操作的场景 |
扩展运算符 | 代码极简 | 仅展开一层 | 快速单层扁平化 |
迭代器遍历 | 避免递归栈溢出,性能稳定 | 实现复杂度较高 | 深层嵌套且数据量大时 |
八、总结
- 基础需求:推荐递归或
reduce
实现,代码简洁且易读。 - 性能敏感场景:使用迭代器遍历或
flat
方法(ES6+)。 - 深度控制:通过递归参数
depth
实现层级控制。 - 类型处理:结合
map(Number)
或parseInt
转换数据类型。