这是某节秋招原题。给的参考实例是:[1, 2, [3, 4, [5, { a: 1 }, [6]]]]。基本思路就是遍历数组拿到每一项,只要每一项的类型是数组就继续遍历扁平化。
这道题看着复杂,里面不仅有数组还有对象。但是其实对象放里面就是唬人的,因为我们在判断是否是数组时不要用typeof就可以避开对象了,用Array.isArray()或者instanceof就ok啦。
实现方式
1.巧用reduce
reduce + 递归
因为reduce函数的参数是previou(上一个)和current(当前),非常适合对数组的元素进行连续操作的情况。
举一个简单的例子熟悉reduce函数:
//对数组元素求和
function sum(arr) {
return arr.reduce((prev, curr) => {
return prev + curr; //一开始prev为初始值0,curr为arr[0]
}, 0); //这是prev的初始值
}
对数组扁平化的话,也是需要对数组进行连续操作的,前一个元素concat后一个元素。
function flat(arr) {
return arr.reduce((prev, curr) => {
return prev.concat(Array.isArray(curr) ? flat(curr) : curr);
}, []);
}
2.巧用正则
将数组扁平化就是要将数组中嵌套的数组拿到最外层数组,对吧?这里有一个很巧的思路就是将数组变为string类型用正则把所有的[]去掉。
function flat(arr) {
arr = '[' + JSON.stringify(arr).replace(/\[|\]/g, "") + ']';
return JSON.parse(arr);
}
还有一种方法只能用于元素中没有对象的数组,因为toString和join在转换时会将对象转为[Object Object]形式。
toString/join +split的原理和上一个差不多,将数组转为string类型,然后通过split分割的形式去拿出元素。
function flat(arr) {
return arr.toString().split(",").map(v => {
return parseInt(v);
})
}
3.扩展运算符+concat
ES6的扩展运算符...能通过迭代器拿出数组里的元素,concat在连接两个数组时也会拿出后一个数组里的元素。
[].concat(...[1,[2,[3]]]) //[1,2,[3]]
利用这个特性可以不用递归形式,如果元素是数组就用...展开,直至没有数组。
function flat(arr) {
while(arr.some(item => Array.isArray(item))) { //some每一次都遍历整个arr比较耗时间,所以不是很推荐这个
arr = [].concat(...arr);
}
return arr;
}
4.纯正递归
新开一个空间res进行递归。
function flat(arr) {
let res = [];
arr.map(v => {
if(v instanceof Array) {
res = res.concat(flat(v));
}else{
res.push(v);
}
});
return res;
}