5

前端百题——竟然有五种方式实现flat方法

 3 years ago
source link: https://my.oschina.net/u/5079097/blog/5085481
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

不知道老铁们有没有遇到过一道面试题:如何将一个多维数组展开成一个一维数组?当时我遇到的时候还不了解flat这个神奇的方法,用了最传统的解决方法进行解决。

const flatten = arr => arr.toString().split(',').map(item => +item);

const arr = [1, 2, [3, 4, [5, 6]]];
console.log(flatten(arr)); // [ 1, 2, 3, 4, 5, 6 ]
复制代码

上述方法是不是很神奇,会将多层级的数组展开成为一个层级,但是该方式其实存在很大问题的,下面让我们一起看看这些问题。

  1. 不管多少层级都会展开为一个层级;
  2. 处理后的结果其实都是字符串,需要后续再转换为原来的类型。

正是基于这个契机,发现了ES6新增了flat函数,这个函数天生就是为数据扁平化处理而生的。

2 flat基础

flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。

  1. flat方法的用法如下所示:
const newArray = arr.flat([depth])
复制代码
const arr = [1, 2, [3, 4, [5, 6]]];
console.log(arr.flat(1)); // [ 1, 2, 3, 4, [ 5, 6 ] ]
console.log(arr.flat(2)); // [ 1, 2, 3, 4, 5, 6 ]
复制代码

flat这么香,那么我们是否可以自己实现一个呢?实现该方法的方式有很多,下面就让我们一起看看这五种方式。(注:这五种方式试MDN上给出的替代方案)

3.1 使用reduce和concat

该方式实现起来虽然很简单,但是存在一个很大的缺陷:只能展开一层,对于多层的情况将无能为力。其思想总结起来为以下两个步骤:

  1. 利用reduce函数去依次处理每个数组中的元素;
  2. 利用concat将当前的数组元素(值或子数组)添加到结果数组中。
// 使用reduce和concat
Array.prototype.flat1 = function () {
    return this.reduce((acc, val) => acc.concat(val), []);
}
复制代码

3.2 使用reduce + concat + isArray + recursivity

该方式已经具备展开多层的能力了,其实现思想可总结为以下几点:

  1. 利用reduce函数去依次处理每个数组中的元素;
  2. 利用concat将当前元素添加到结果数组中;
  3. 利用isArray判断当前数组中的元素是不是一个数组;
  4. 利用递归思想展开多层级的数组。
// 使用reduce + concat + isArray +recursivity
Array.prototype.flat2 = function (deep = 1) {
    const flatDeep = (arr, deep = 1) => {
        return deep > 0 ? arr.reduce((acc, val) => acc.concat(Array.isArray(val) ? flatDeep(val, deep - 1) : val), []) : arr.slice();
    }

    return flatDeep(this, deep);
}
复制代码

3.3 使用forEach + concat + isArray +recursivity

该方式与上述方式很类似,能够设定层级展开,只是遍历数组由reduce转换为forEach。

// 使用forEach + concat + isArray +recursivity
// forEach 遍历数组会自动跳过空元素
Array.prototype.flat3 = function (deep = 1) {
    const result = [];
    (function flat(arr, deep) {
        arr.forEach((item) => {
            if (Array.isArray(item) && deep > 0) {
                flat(item, deep - 1);
            } else {
                result.push(item);
            }
        })
    })(this, deep);

    return result;
}
复制代码

3.4 使用for of + concat + isArray +recursivity

该方式与上述方式很类似,能够设定层级展开,只是遍历数组利用了for of方法

// 使用for of + concat + isArray +recursivity
// for of 遍历数组会自动跳过空元素
Array.prototype.flat4 = function (deep = 1) {
    const result = [];
    (function flat(arr, deep) {
        for(let item of arr) {
            if (Array.isArray(item) && deep > 0) {
                flat(item, deep - 1);
            } else {
                // 去除空元素,因为void 表达式返回的都是undefined,不适用undefined是因为undefined在局部变量会被重写
                item !== void 0 && result.push(item);
            }
        }
    })(this, deep);

    return result;
}
复制代码

3.5 使用堆栈stack

该方式主要利用堆栈的思想,将一个多层数组全部展开为一层。其思想可总结为以下几个步骤:

  1. 将要处理的数组放到一个栈中处理;
  2. 从栈顶取出元素,判断该元素类型,若为数组,则将该数组展开再放回栈顶;若为普通元素则将其放到结果中;
  3. 循环遍历,至到栈为空。
// 使用堆栈stack
Array.prototype.flat5 = function() {
    const stack = [...this];
    const result = [];
    while (stack.length > 0) {
        const next = stack.pop();
        if (Array.isArray(next)) {
            stack.push(...next);
        } else {
            result.push(next);
        }
    }

    // 反转恢复原来顺序
    return result.reverse();
}
复制代码

1.如果觉得这篇文章还不错,来个分享、点赞吧,让更多的人也看到

如果你觉得这篇文章对你有点用的话,麻烦请给我们的开源项目点点star:        http://github.crmeb.net/u/defu         不胜感激 !
来自 “开源世界 ” ,链接:      https://ym.baisou.ltd/post/737.html ,如需转载,请注明出处,否则将追究法律责任。 ​​​​​​

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK