如何实现数组扁平化?flat。还有呢?

303 阅读6分钟

引言

本文介绍了将嵌套数组转换为一维数组的七种常见方法:flat()方法、递归、toString()方法、reduce()方法、扩展运算符flatMap()方法和堆栈。简要分析了它们的优缺点,帮助读者选择最适合其情景的方法。

案例引入

简而言之,数组扁平化就是将一个多维数组变成一个一维数组,去除其中的嵌套数组,让数据变得更加扁平,方便处理和遍历。在实际开发中,经常需要对多维数组进行扁平化操作,例如处理JSON数据、递归操作等。

想象一下,你手头上有一个嵌套的数组,里面包含着各种各样的元素,而你现在需要将这个多层嵌套的数组整理成一个简单的一维数组。

初始:

const arr = [1, 2, [3, 4, [5]]]

变成:

[ 1, 2, 3, 4, 5 ]

常见数组扁平化方法

1. flat()方法

flat()方法是ES6新增的数组方法,可以直接将多维数组一次性扁平化成指定深度的一维数组,非常方便。

优点:

  • 简洁直观,易于使用。
  • 性能优异,对于大部分情况都能有效处理。

缺点:

  • 不支持浏览器兼容性较差的环境。
  • 不能定制化处理,仅支持固定深度或无限深度。
const arr = [1, 2, [3, 4, [5]]]

const newArr = arr.flat(1) // 数组扁平化
const newArr1 = arr.flat(Infinity) //无穷大

console.log(newArr);
console.log(newArr1);

image.png

  • arr.flat(1) 展开一层嵌套数组。
  • arr.flat(Infinity) 完全展开所有层级的嵌套数组。

2. 递归

递归是一种经典的扁平化方法,通过遍历数组的每一项,如果是数组则递归调用自身直到将所有元素提取出来。

优点:

  • 逻辑清晰,容易理解。
  • 适用于任意层级的数组。

缺点:

  • 在深度层级非常高的情况下可能会导致栈溢出。
  • 性能较差,尤其是对大型数组。
const arr = [1, 2, [3, 4, [5]]]

function flatten(arr) {
    let res = []
    for (let i = 0; i < arr.length; i++) {
        if (Array.isArray(arr[i])) {
            res.push(...flatten(arr[i])) //递归
        } else {
            res.push(arr[i]);
        }
    }
    return res;

}

const newArr = flatten(arr)
console.log(newArr);

image.png 递归执行过程概述:

  1. 最外层数组 [1, 2, [3, 4, [5]]] 传入 flatten 函数:

    • 1 和 2 被直接添加到结果数组 res
    • 对 [3, 4, [5]] 递归调用 flatten
  2. 在递归调用中 [3, 4, [5]] 被传入 flatten 函数:

    • 3 和 4 被直接添加到结果数组 res
    • 对 [5] 递归调用 flatten
  3. 在最内层递归调用中 [5] 被传入 flatten 函数:

    • 5 被直接添加到结果数组 res
  4. 最内层的 flatten 调用返回 [5],中层递归合并得到 [3, 4, 5],再返回至最外层,合并所有结果得到 [1, 2, 3, 4, 5]

3. toString()方法

将数组转换成字符串,然后再通过split()方法拆分,最后再转换成数字类型,这样就可以实现数组扁平化了。

优点:

  • 方法简单,代码短小。

缺点:

  • 无法处理非数字数组元素。
  • 会将非预期的字符类型也拆分。
  • 性能较差。
const arr = [1, 2, [3, 4, [5]]]

let str = arr.toString()  // '1,2,3,4,5'
const newArr = str.split(',').map((item) => {

  return Number(item)

})

console.log(newArr);

image.png

  1. arr 被转换为字符串 '1,2,3,4,5'
  2. 字符串根据逗号分割为数组 [ '1', '2', '3', '4', '5' ]
  3. 每个字符串元素被转换为数字类型,生成最终的数组 [1, 2, 3, 4, 5]

4. reduce() 方法

利用reduce()方法不断将数组拆分成单个元素,然后再合并起来,最终实现数组扁平化。

优点:

  • 函数化编程方式,代码简洁。
  • 可控制扁平化的过程。

缺点:

  • 对于深层次数组性能一般。
  • 代码可读性对于复杂嵌套的情况较差。
const arr = [1, 2, [3, 4, [5]]]

function flattenArray(arr) {
    return arr.reduce((pre, val) => Array.isArray(val) ? pre.concat(flattenArray(val)) : pre.concat(val), []);
}

const newArr = flattenArray(arr);

console.log(newArr);

flattenArray函数的作用是使用 reduce 方法递归扁平化多层嵌套的数组。

  1. reduce 方法传入初始值 [],并通过累积器 pre 和当前值 val 进行迭代。

  2. 对于数组中的每个元素 val,检查是否为数组:

    • 如果是数组,则递归调用 flattenArray(val) 直到所有嵌套数组被扁平化。
    • 否则,将当前值 val 添加到累积器 pre 中。
  3. concat 方法用于连接累积器 pre 和处理过的值,确保每个嵌套数组都被逐层展开。

5. 使用扩展运算符

通过ES6新增的扩展运算符,可以将多个数组“展开”成单个数组,实现扁平化的效果。

优点:

  • 代码简洁、直观。
  • 可处理任意深度的数组。

缺点:

  • 在深层次数组的情况下性能较差。
  • 不支持非常旧的浏览器环境。
const arr = [1, 2, [3, 4, [5]]]

function flattenArray(arr) {
    while (arr.some(Array.isArray)) {
        arr = [].concat(...arr);
    }
    return arr;
}

const newArr = flattenArray(arr);

console.log(newArr);

使用 while 循环来扁平化嵌套数组:

  1. arr.some(Array.isArray):检查数组 arr 中是否还存在数组。

  2. [].concat(...arr):将嵌套数组展开一层,通过 concat 和展开操作符 ... 实现。

  3. 循环会继续,直到所有嵌套的数组都被扁平化为一维数组。

[1, 2, [3, 4, [5]]] while一次变为 [1, 2, 3, 4, [5]]

6. flatMap() 方法

flatMap()方法在将每个元素映射为一个新数组后,再将这些新数组“压缩”为一个新数组。

优点:

  • 方法简洁,现代化。
  • 可结合映射和扁平化的功能。

缺点:

  • 不支持非常旧的浏览器环境。
  • 需要自己实现递归逻辑。
const arr = [1, 2, [3, 4, [5]]]

function flattenArray(arr) {
    return arr.flatMap(item => Array.isArray(item) ? flattenArray(item) : item);
}

const newArr = flattenArray(arr);

console.log(newArr);

flattenArray函数利用 flatMap 函数来扁平化嵌套数组:

  1. flatMap 方法在映射每个元素时,会将返回的数组展开到一级,从而实现数组扁平化。

  2. 对于数组中的每个 item,检查是否为数组:

    • 如果是数组,则递归调用 flattenArray(item) 直到所有嵌套数组被扁平化。
    • 否则,返回当前元素 item
  3. 最终得到一个扁平化后的数组。

const arr = [1, 2, [3, 4, [5]]];
const newArr = arr.flatMap((x) => x);

console.log(newArr); // [ 1, 2, 3, 4, [ 5 ] ]

7. 堆栈(模拟递归)

使用堆栈的数据结构来模拟递归,将需要处理的数组不断压栈再出栈,直到最终扁平化。

优点:

  • 不会导致栈溢出,适合非常深的数组。
  • 性能较好,尤其是处理大型数组时。

缺点:

  • 较复杂的实现方式,代码可读性稍差。
  • 需要手动管理堆栈。
const arr = [1, 2, [3, 4, [5]]]

function flattenArray(arr) {
    const stack = [...arr]; // 创建一个栈,初始时包含 arr 的所有元素
    const result = []; // 存储扁平化后的结果数组

    while (stack.length) {
        const next = stack.pop(); // 从栈中取出下一个元素
        
        // 如果下一个元素是数组,则将其展开,并放回栈中;否则放入结果数组的开头
        Array.isArray(next) ? stack.push(...next) : result.unshift(next);
    }

    return result;
}
const newArr = flattenArray(arr);

console.log(newArr);

flattenArray函数利用栈数据结构来扁平化嵌套数组:

  1. 创建一个栈 stack,初始时包含原始数组 arr 的所有元素。

  2. 创建一个空数组 result,用于存储扁平化后的结果。

  3. 在循环中,不断从栈中取出元素:

    • 如果取出的元素是数组,则展开数组并放回栈;
    • 如果不是数组,则将其放入结果数组的开头。
  4. 最终返回扁平化后的结果数组。

结语

在JavaScript中,数组扁平化是一个常见的操作,不同的方法适用于不同的情况。希望通过本篇文章的介绍,可以帮助你更加灵活地处理数组扁平化的问题。