手撕数组扁平化的N种方式(Javascript 版)

8 阅读2分钟

数组扁平化(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 转换数据类型。