likes
comments
collection
share

『 干货』实现数组扁平化的 6 种方式

作者站长头像
站长
· 阅读数 10

大家好,我是爱吃鱼的桶哥Z,在前面一节中我们梳理了数组中眼花缭乱的API,而在日常的开发中,我们经常会需要将数组做扁平化处理,以方便我们的开发,那么如何实现数组扁平化呢?

首先我们先了解一下数组扁平化的应用场景,数组扁平化一般在一些多维数组的应用中会出现,因为操作多维数组会较为麻烦,因此将多维数组扁平化变为一维数组后,将大大简化我们对数组的操作,这一节我们就一起来学习一下关于数组扁平化的 6 种方式吧!

先思考

首先,我们还是先带着问题来学习,问题如下:

  1. 如何通过最普通的方法来解决数组扁平化问题?
  2. ES6 中是否有一些高级的方法能够直接实现数组扁平化?

数组扁平化

上面说了这么多,还没有介绍 数组扁平化 是什么?数组的 扁平化 其实就是将一个多层嵌套的数组转换为只有一层的数组,我们可以通过一个例子来看一下,代码如下:

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

// 伪代码
console.log(flatten(arr)); // [1, 2, 3, 4, 5]

上述代码中,我们通过 flatten 方法将多维数组转换为一维数组,这个方法就是数组扁平化,那么该如何实现 flatten 呢?下面我们实现一下。

普通递归实现

通过循环递归的方法,一项一项的去遍历,如果数组中的每一项都还是一个数组,那么就继续遍历下去。利用递归调用的方法,来实现数组中的每一项的连接,让我们一起来看一下代码,如下:

// 递归调用
const arr = [1, [2, [3, 4, 5]]];

const 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;
};

flatten(arr); // [1, 2, 3, 4, 5]

上述代码中,通过循环遍历数组,判断数组中的每一项是否是数组,如果当前元素不是数组,则直接添加到一个临时存储的空数组中,然后通过递归不断来执行这个数组,直到数组中的所有元素都执行完毕,则会返回整个扁平化后的数组。

利用 reduce 函数迭代

通过上面的普通递归函数可以发现,其实就是对数组中的每一项进行处理,这跟前面一节中说的 reduce 来实现数组的拼接很类似,因此这里我们可以使用 reduce 来实现函数迭代,从而简化第一种方法,具体的代码如下:

// reduce 函数
const arr = [1, [2, [3, 4, 5]]];

const flatten = (arr) => {
    return arr.reduce((prev, next) => {
        return prev.concat(Array.isArray(next) ? flatten(next) : next)
    }, []);
};

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

上面的代码中,通过 reduce 来遍历数组,其内部还是判断每一项是否是一个数组,如果不是就累加,否则就继续执行 flatten,其实思路跟第一种的递归的一样的,只是实现起来相对简单一些。

扩展运算符实现

在上面我们通过 reduce 方法来实现数组扁平化,那么是否还有更简单的办法呢?答案是有的,那就是使用 扩展运算符实现 ,我们一起来看一下具体的实现代码,如下:

// 扩展运算符
const arr = [1, [2, [3, 4, 5]]];

const flatten = (arr) => {
    while (arr.some(item => Array.isArray(item))) {
        arr = [].concat(...arr);
    }
    return arr;
};

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

在上面的方法中,通过 while 循环,判断每一项是否是数组,如果是就执行 while 循环内部的语句,通过 扩展运算符 将每一项不是数组的内容添加到数组中,最后当 while 条件不成立时,就代表数组中的每一项都已经被拍平了。

上面的三种实现数组扁平化的方式都是最基本的思路,都是通过最普通的递归思路衍生的方法来实现的,尤其是前两种比较类似,值得注意的是 reduce 方法,它可以在很多场景中应用实现。由于 reduce 方法提供的几个参数比较灵活,能够解决很多问题,因此是值得我们熟练掌握并精通的一个方法。

那么除了上面的三种方法,是否还有其他的实现思路呢?让我们接着往下看。

使用 split 和 toString 共同处理

由于数组默认带有一个 toString 的方法,因此我们可以将数组转换为带逗号的字符串,然后再使用数字的 split 方法将字符串进行切割,具体的代码如下:

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

const flatten = (arr) => {
    return arr.toString().split(',');
};

console.log(flatten(arr)); // ['1', '2', '3', '4', '5']

通过 toStringsplit 对数组进行转换和切割后,最后将数组打平,但是数组中的数字变成了字符串,因此这种方法其实是有一定的破坏性,它会将数组中的数据类型进行修改。

ES6 中的 flat

除了上述通过 toStringsplit 对数组进行处理外,我们还可以直接使用 ES6 中新增的 flat 方法来实现数组的扁平化,先看一下 flat 的使用语法:arr.flat([depth]),其中 depth 是代表数组可以展开的深度,也就是可以展开几层数组。展开的层数是根据 depth 决定的,如果是多层的话,只需要传入 Infinity ,代表无论是多少层都会展开,下面我们一起来看一下用 flat 怎么实现数组扁平化,代码如下:

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

const flatten = (arr) => {
    return arr.flat(Infinity)
};

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

上面的代码中,我们在 flat 内部设置的参数是 Infinity,其实也可以传入一个 3,因此数组的层数是3层,因此 flat 的参数其实也可以是数组本身的层数,如果我们不知道数组有多少层,那么使用 Infinity 就可以了。

使用正则和 JSON 方法共同处理

除了上面说的这些方法外,我们还可以通过正则和 JSON 中的 stringify 来做数组扁平化,让我们一起来看代码,如下:

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

const flatten = (arr) => {
    let str = JSON.stringify(arr);
    str = str.replace(/(\[|\])/g, '');
    str = '[' + str + ']';
    return JSON.parse(str);
};

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

在上述的代码中,我们首先通过 JSON.stringify 将数组转换为字符串,然后通过正则表达式将数组中的方括号过滤掉,最后将替换后的字符串通过 JSON.parse 方法转换为数组。

以上就实现了6中数组扁平化的方法,你还能想出其它的解决方案吗?

最后

在这篇文章中,我们学习了常见的6种数组扁平化的方法,在实际开发中我们可以运用这篇文章中讲到的知识点来将多维数组转换为一维数组,这样操作起来会更加的简便。当然,日常开发中不可能是存储的数组,有时候也是多层的数组对象,那么遇到数组对象我们该如何做数组的扁平化呢?这个问题你可以自己思考一下,我也会在评论区留下最后这个问题的答案。

我们学完这6种方法后,那么最前面的两个问题,你现在知道该如何解答了吧!

最后,如果这篇文章有帮助到你,❤️关注+点赞❤️鼓励一下作者,谢谢大家

参考文档

Array.prototype.flat()

往期回顾

『 纯干货』帮你梳理总结眼花缭乱的数组 API

如何实现 new、apply、call、bind ?这篇文章告诉你

JS 中这些继承方式你知道吗?

箭头函数能作为事件监听的回调函数吗?

用操作数组的方式来操作对象,该怎么做?

这几个数组的操作方法你必须知道

JS 中的类数组,是时候了解了