Facebook面试题: 用递归和迭代手写Array.prototype.flat()
题目来源: bigfrontend.dev/problem/imp…
手写 Array.prototype.flat()
看似简单,但是Facebook面试一轮通常有两道题目,也就是说要在15分钟之内写出bugfree的代码,加上现场的紧张已经还要求清晰的思路表达,还是有挑战的。
题目
我们需要实现flat()
来去掉括号,层数由第二个参数来指定
const arr = [1, [2], [3, [4]]];
flat(arr)
// [1, 2, 3, [4]]
flat(arr, 1)
// [1, 2, 3, [4]]
flat(arr, 2)
// [1, 2, 3, 4]
复制代码
递归
用recursion比较简单了,便利数组做如下操作即可:
- 如果碰到了array,就先用
flat()
去掉它的括号,然后收集结果 - 如果碰到的不是array,就收集结果
用以上想法就可以简单地得到以下代码。
function flat(arr, depth = 1) {
const result = []
for (let item of arr) {
if (Array.isArray(item) && depth > 0) {
result.push(...flat(item, depth - 1))
} else {
result.push(item)
}
}
return result
}
复制代码
用reduce重写
有一个result,有一个for loop,那就可以用reduce玩一点花样出来。 不过这个并不没什么卵用,不是本质上的变化。
function flat(arr, depth = 1) {
return arr.reduce((result, item) => {
if (Array.isArray(item) && depth > 0) {
result.push(...flat(item, depth - 1))
} else {
result.push(item)
}
return result
}, [])
}
复制代码
迭代
刷过面试题都知道,遇到递归就要尝试一下用迭代来写,可以说必考了。
我们先手写一下去掉括号的过程。左边是目标array,右边是结果array。
[1, [2], [3, [4]]] => []
复制代码
从左往右,碰到了1,不是array,直接放入结果
[[2], [3, [4]]] => [1]
复制代码
下一个是 [2]
,是array,去掉括号,重新放回原来的数组。
[2, [3, [4]]] => [1]
复制代码
下一个是2,不是array,放入结果
[[3, [4]]] => [1,2]
复制代码
重复上述过程,我们得到以下中间结果。
[3, [4]] => [1,2]
[[4]] => [1,2,3]
[4] => [1,2,3]
[] => [1,2,3,4]
复制代码
接下来得考虑depth
的问题,我们不能见到括号就去掉,所以每个element需要有自己的step,相当于给原数组的element增加了一个depth属性,我们用 [element, depth]
来表示。
假设depth是1: 初始状态下所有元素都是depth:1
[[1, 1], [[2], 1], [[3, [4]], 1]] => []
复制代码
下一个element是1,不是array,所以放入结果
[[[2], 1], [[3, [4]], 1]] => [1]
复制代码
下一个element是[2]
,是array,depth是1,那么它的所有元素的depth设为0,然后重新放回
[[2, 0], [[3, [4]], 1]] => [1]
复制代码
下一个element是2, 不是array,放入结果
[[[3, [4]], 1]] => [1, 2]
复制代码
下一个是 [3,[4]]
, depth是1,把其中元素放回,depth设为0
[[3,0],[[4],0]] => [1, 2]
复制代码
下一个是3,直接放入结果
[[[4],0]] => [1, 2, 3]
复制代码
下一个是 [4]
,depth是0,因为是0,所以这个括号没法去掉,把[4]
放入结果
[] => [1,2,3,[4]]
复制代码
大功告成。
把上述过程转换微代码,就比较容易了。以下是迭代的解法。
function flat(arr, depth = 1) {
const result = []
const stack = [...arr.map(item => ([item, depth]))]
while (stack.length > 0) {
const [head, depth] = stack.shift()
if (Array.isArray(head) && depth > 0) {
stack.unshift(...head.map(item => ([item, depth - 1])))
} else {
result.push(head)
}
}
return result
}
复制代码
这里还有一个performance的问题。我们用到了shift/unshift
, 实际上是不太好的,因为shift/unshift
会触发所有元素的index发生变化。更好的办法是用 push/pop
,这样我们得到了一个逆序的结果,最后reverse一下就好。
function flat(arr, depth = 1) {
const result = []
const stack = [...arr.map(item => ([item, depth]))]
while (stack.length > 0) {
const [top, depth] = stack.pop()
if (Array.isArray(top) && depth > 0) {
stack.push(...top.map(item => ([item, depth - 1])))
} else {
result.push(top)
}
}
return result.reverse()
}
复制代码
通过撒花! 如果你有兴趣可以去 BFE.dev 实际挑战一下。
希望能帮助到你!接下来JSer还会带来更多前端面试代码题解析,欢迎关注。
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 学习在kernel态下使用NEON对算法进行加速的方法
- Centos7上Mesos和Marathon的安装和配置
- Redis高级数据类型-Bitmap和HyperLogLog
- Redis持久化策略
- 在linux下开启FTP服务方法介绍
- Linux中gpio接口的使用方法示例
- Three.js教程(1):初识three.js
- 解决Electron安装报错问题
- Linux端口映射转发的方法
- Centos7如何备份和还原Redis数据的方法
- linux contos6.8下部署kafka集群的方法
- 基于Electron的touchbar开发
- Redis数据结构-简单动态字符串
- 使用new Function创建async方法
- Canvas系列(14):实战-小球碰撞