前端百题——竟然有五种方式实现flat方法
1 背景
不知道老铁们有没有遇到过一道面试题:如何将一个多维数组展开成一个一维数组?当时我遇到的时候还不了解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 ] 复制代码
上述方法是不是很神奇,会将多层级的数组展开成为一个层级,但是该方式其实存在很大问题的,下面让我们一起看看这些问题。
- 不管多少层级都会展开为一个层级;
- 处理后的结果其实都是字符串,需要后续再转换为原来的类型。
正是基于这个契机,发现了ES6新增了flat函数,这个函数天生就是为数据扁平化处理而生的。
2 flat基础
flat()
方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。
- 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 ] 复制代码
3 实现
flat这么香,那么我们是否可以自己实现一个呢?实现该方法的方式有很多,下面就让我们一起看看这五种方式。(注:这五种方式试MDN上给出的替代方案)
3.1 使用reduce和concat
该方式实现起来虽然很简单,但是存在一个很大的缺陷:只能展开一层,对于多层的情况将无能为力。其思想总结起来为以下两个步骤:
- 利用reduce函数去依次处理每个数组中的元素;
- 利用concat将当前的数组元素(值或子数组)添加到结果数组中。
// 使用reduce和concat Array.prototype.flat1 = function () { return this.reduce((acc, val) => acc.concat(val), []); } 复制代码
3.2 使用reduce + concat + isArray + recursivity
该方式已经具备展开多层的能力了,其实现思想可总结为以下几点:
- 利用reduce函数去依次处理每个数组中的元素;
- 利用concat将当前元素添加到结果数组中;
- 利用isArray判断当前数组中的元素是不是一个数组;
- 利用递归思想展开多层级的数组。
// 使用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
该方式主要利用堆栈的思想,将一个多层数组全部展开为一层。其思想可总结为以下几个步骤:
- 将要处理的数组放到一个栈中处理;
- 从栈顶取出元素,判断该元素类型,若为数组,则将该数组展开再放回栈顶;若为普通元素则将其放到结果中;
- 循环遍历,至到栈为空。
// 使用堆栈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.如果觉得这篇文章还不错,来个分享、点赞吧,让更多的人也看到

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
HarmonyOS学习路之开发篇——Intent
Intent 基本概念 Intent是对象之间传递信息的载体。例如,当一个Ability需要启动另一个Ability时,或者一个AbilitySlice需要导航到另一个AbilitySlice时,可以通过Intent指定启动的目标同时携带相关数据。Intent的构成元素包括Operation与Parameters,具体描述参见表1。 表1 Intent的构成元素 当Intent用于发起请求时,根据指定元素的不同,分为两种类型: 如果同时指定了BundleName与AbilityName,则根据Ability的全称(例如“com.demoapp.FooAbility”)来直接启动应用。 如果未同时指定BundleName和AbilityName,则根据Operation中的其他属性来启动应用。 说明 Intent设置属性时,必须先使用Operation来设置属性。如果需要新增或修改属性,必须在设置Operation后再执行操作。 关于Intent最简单的使用方法,可参见快速入门的示例代码。其中“实现页面跳转”重点描述了使用Intent实现两个页面跳转关系的操作。 根据Ability的全称...
- 下一篇
Redis:我是如何与客户端进行通信的
江湖上说,天下武功,无坚不摧,唯快不破,这句话简直是为我量身定制。 我是一个Redis服务,最引以为傲的就是我的速度,我的 QPS 能达到10万级别。 在我的手下有数不清的小弟,他们会时不时到我这来存放或者取走一些数据,我管他们叫做客户端,还给他们起了英文名叫 Redis-client。 有时候一个小弟会来的非常频繁,有时候一堆小弟会同时过来,但是,即使再多的小弟我也能管理的井井有条。 有一天,小弟们问我。 想当年,为了不让小弟们拖垮我傲人的速度,在设计和他们的通信协议时,我绞尽脑汁,制定了下面的三条原则: 实现简单 针对计算机来说,解析速度快 针对人类来说,可读性强 为什么这么设计呢?先来看看一条指令发出的过程,首先在客户端需要对指令操作进行封装,使用网络进行传输,最后在服务端进行相应的解析、执行。 这一过程如果设计成一种非常复杂的协议,那么封装、解析、传输的过程都将非常耗时,无疑会降低我的速度。什么,你问我为什么要遵循最后一条规则?算是对于程序员们的馈赠吧,我真是太善良了。 我把创造出来的这种协议称为 RESP (REdis Serialization Protocol)协议,它工...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- CentOS7设置SWAP分区,小内存服务器的救世主
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS6,CentOS7官方镜像安装Oracle11G
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS关闭SELinux安全模块
- CentOS8编译安装MySQL8.0.19
- CentOS7安装Docker,走上虚拟化容器引擎之路
- Windows10,CentOS7,CentOS8安装Nodejs环境