前言
因为es2019对数组增加了flat方法,最近讨论flat的频率也有所提高,各位大佬各显神通已经写了很多,如果我今天在这讨论flat有几种递归写法,那就没有什么意义了,所以今天我们讨论些不同的,我们讨论怎么写出迭代的flat
提出问题
先提出4个问题
- 你能实现一个将数组展平的函数吗?
- 你能实现一个可以传入展平深度参数的flat函数吗?
- 你能实现一个迭代版本的数组展平函数吗?
- 你能实现一个迭代版本的可传入展平深度参数的flat函数吗?
你先思考下,如果你都能写出来,就不用继续浪费时间啦?5s后揭开答案
5
4
3
2
1
flat递归版本
function flat(arr) {
return arr.reduce((pre, cur) => {
return [...pre, ...(Array.isArray(cur) ? flat(cur) : [cur])];
}, []);
}
复制代码
带depth参数的flat递归版本
function flat(arr, depth = 1) {
return depth > 0
? arr.reduce((pre, cur) => {
return [...pre, ...(Array.isArray(cur) ? flat(cur, depth - 1) : [cur])];
}, [])
: arr;
}
复制代码
接下来是重点
flat迭代版本
其实就是树的前序遍历迭代写法,有一点需要注意的是,栈是后进先出的,所以在压栈的时候要从数组的最后一个元素开始压,这样弹出的顺序就是从数组的本身的顺序了。
function flat(arr, depth = 1) {
let ret = [];
let s = [];
if (arr.length > 0) {
for (let i = arr.length - 1; i >= 0; i--) {
s.push(arr[i]);
}
}
while (s.length > 0) {
let cur = s.pop();
if (Array.isArray(cur)) {
for (let i = cur.length - 1; i >= 0; i--) {
s.push(cur[i]);
}
} else {
ret.push(cur);
}
}
return ret;
}
复制代码
带depth参数的flat递归版本
我们在递归的时候函数调用栈可以保存我们的状态,这样我们就不用额外记录当前递归的深度了,但是迭代的时候呢?我们怎样保存当前栈顶元素被faltten
了几次呢?我想的一个可选的方法是用另一个栈来保存当前栈顶元素被flatten了几次如果到达了指定的次数直接返回即可,如果栈顶元素是数组需要继续faltten,那么将栈顶的状态减一即可。最终实现的效果就是始终保证两个栈的栈顶是一一对应的关系。
function flat(arr, depth = 1) {
let ret = [];
let s = [];
let status = [];
if (arr.length > 0) {
for (let i = arr.length - 1; i >= 0; i--) {
s.push(arr[i]);
status.push(depth);
}
}
while (s.length > 0) {
let cur = s.pop();
let curStatus = status.pop();
if (Array.isArray(cur) && curStatus > 0) {
for (let i = cur.length - 1; i >= 0; i--) {
s.push(cur[i]);
status.push(curStatus - 1);
}
} else {
ret.push(cur);
}
}
return ret;
}
复制代码
后记
最后,再见flatten
了