一、什么是数组扁平化
数组扁平化,就是将一个复杂的嵌套多层的数组,一层一层的转化为层级较少或者只有一层的数组。
比如:
// 多维数组
var arr = [1,2,[3,4,5,[6,7,8],9],10,[11,12]];
// 数组扁平化
[1,2,3,4,5,6,7,8,9,10,11,12]
二、实现方式
1. Array.prototype.flat([depth])
使用ES2019中新增的
Array.prototype.flat([depth])
方法,该方法不会改变原数组,会返回一个新数组。
这个方法接收的可选参数depth
,表示扁平的深度,默认值为 1,表示扁平至一层的深度。
参数Infinity
表示完全展开。
[1,2,[3]].flat()
// [1, 2, 3]
[1,2,[3,[4]]].flat(2)
// [1, 2, 3, 4]
// 终极解决方案
function flatten(arr) {
return arr.flat(Infinity);
}
var arr = [1,2,[3,4,5,[6,7,8],9],10,[11,12]];
flatten(arr);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
关于flat
-
完整语法:
array.flat([depth]);
-
扁平化嵌套了很多层的数组
不知道要扁平的数组的具体深度,只想完全扁平这个嵌套数组里的成员话,可以使用Infinity
这个值 -
忽略数组空位
flat()
方法有一个比较 cool 的地方在于,扁平数组的同时,还能移除数组中的空位(Empty Slots)。const arr = [1, ,3, ,5]; arr.flat(); // [1, 3, 5];
-
浏览器支持
flat() 是 ES2019 里定义的新特性,因此还是请大家忘记 Internet Explorer 和 Edge 吧😅
2. 递归「for循环」
思路:采用普通递归,很好理解,通过
for
循环的方式,逐层逐个元素地去展平,如果当前元素不是数组,就执行push
操作,是数组的话,就进行递归处理,再将递归处理的结果拼接到结果数组上,直至遍历完整个数组。
function flatten(arr){
let res = [];
if(Object.prototype.toString.call(arr) != "[object Array]"){return false};
for(var i = 0; i < arr.length; i++){
if(arr[i] instanceof Array){
res = res.concat(flatten(arr[i])) // concat 并不会改变原数组
// res.push(...flatten(arr[i])); // ...扩展运算符
}else{
res.push(arr[i])
}
}
return res;
};
var arr = [1,2,[3,4,5,[6,7,8],9],10,[11,12]];
flatten(arr);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
小Tips:
- 数组连接用
concat
,保证数组的原有维度不变; push
会改变原始数据,并且没有铺平数组的功能,你传什么进去,就是什么。会导致数组的维度发生变化,增加了数组的维度;...
和concat()
可以进行替换,可以算是2种方法,但是...
扩展运算符,只适合降低一个维度。
3. while循环
思路:利用
while
判断加上some
的遍历来实现扁平化。
some
目的是判断当前数组是否还有数组元素,如果有则对数组进行一层展开,同时将展开结果作为下一次判断的条件,当循环条件不满足时说明数组里已经没有数组元素了,这是数组已经完全扁平。
function flatten(arr) {
while (arr.some(item => Array.isArray(item))) {
arr = [].concat(...arr);
// arr = [].concat.apply([], arr);
}
return arr;
}
var arr = [1,2,[3,4,5,[6,7,8],9],10,[11,12]];
flatten(arr);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
4. reduce方法
思路:
reduce
是JS数组中非常强大的一个方法,同时也是JS中的一个函数式编程API。
前面的递归实现的关键就是对数组的每一项进行处理,遇到数组就递归处理它,既然需要循环和结果数组,那么我们可以使用reduce
来简化它。
注意
reduce((prev,cur,index,arr)=>{},init)
用法:
接受两个参数,一个callback,一个初始值
- callback的四个参数分别为,上一次 return的结果,当前项,当前索引,数组
- 第二个参数是初始值,也是作为遍历的开始,我们这里设置一个空数组
reduce
实现什么功能取决于回调内部做了什么
function flatten(arr){
if(Object.prototype.toString.call(arr) != "[object Array]"){return false};
return arr.reduce((prev, cur)=>{
return prev.concat(Array.isArray(cur) ? flatten(cur) : cur)
},[])
};
var arr = [1,2,[3,4,5,[6,7,8],9],10,[11,12]];
flatten(arr);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
5. toString + split
前提:要根据元素类型而定,比如数组的项全为数字
思路:先把数组使用toString()
转换成字符串,这个过程会吧[]
去掉,然后再调用split()
方法转换成数组,最后把每一项转换为数组,即调用map()
方法。
原理:
toString
是Object
原型链上的一个方法,由于JS中所有对象都派生自Object
对象,所以它们都能调用toString
方法,只不过不同的对象可能会对这个方法进行改写以输出自己想要的格式。
数组的toString
方法会将数组转换成一个元素间以逗号相隔的字符串,它内部会先将数组展平成一维后再转换成字符串,因此我们可以先利用toString
进行展平,然后再通过split
方法以逗号分隔每个元素来复原一个包含所有元素的数组,从而实现数组的扁平化。
注意:该方法具有一定的局限性,对于包含引用类型元素的数组来说,在
toString
过程中会发生类型转换,从而使得转换结果异常,因为对于引用类型转成字符串,会调用引用类型的toString
,上面提到不同对象会对它进行改写,例如函数就会得到一个函数体的代码字符串,而不是我们想要的函数引用。
所以,使用这种方式我们要看元素类型而定。
function flatten(arr) {
return arr.toString().split(',').map(i => Number(i));
}
var arr = [1,2,[3,4,5,[6,7,8],9],10,[11,12]];
flatten(arr);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
6. JSON + 正则实现
思路:利用
JSON.stringify()
以及JSON.parse()
方法,以及string中replace
方法,将数组转换成字符串的表达形式,这里将数组转成字符串我们使用JSON.stringify
方法,将数组转换成一个由括号包裹、元素间以逗号相隔的字符串,最后将括号全部替换成空字符,得到一个元素以逗号相隔字符串,最后将利用JSON.parse
方法解析成数组对象。
function flatten(arr) {
let str = JSON.stringify(arr);
str = str.replace(/(\[|\])/g, '');
// 拼接最外层,变成JSON能解析的格式
str = '[' + str + ']';
return JSON.parse(str);
}
var arr = [1,2,[3,4,5,[6,7,8],9],10,[11,12]];
flatten(arr);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
注意:这种方式在处理引用数据类型上也有局限性,同时还要注意元素是否是JSON的合法数据类型。
小结
数组扁平化的方法是有很多种的,但是大致也可以分为三类:
- ES6 的
falt()
:他的性能是最好的,但是需要考虑浏览器兼容性的问题; - 通过
for()
while()
reduce()
等递归遍历的方式; - 使用
toString
JSON
等转为字符串的方式,去除括号,最终再转为数组的方式;
以上所有的方法demo都可使用,若有其他更好的方式,欢迎大佬补充,若文中有不对的地方,还请指正,及时改进,共同进步。
希望上面的内容对你的工作学习有所帮助!欢迎各位一键三连哦~
各位 加油!
✨ 原创不易,还希望各位大佬支持一下 \textcolor{blue}{原创不易,还希望各位大佬支持一下} 原创不易,还希望各位大佬支持一下
👍 点赞,你的认可是我创作的动力! \textcolor{green}{点赞,你的认可是我创作的动力!} 点赞,你的认可是我创作的动力!
⭐️ 收藏,你的青睐是我努力的方向! \textcolor{green}{收藏,你的青睐是我努力的方向!} 收藏,你的青睐是我努力的方向!
✏️ 评论,你的意见是我进步的财富! \textcolor{green}{评论,你的意见是我进步的财富!} 评论,你的意见是我进步的财富!