JavaScript 常用数组方法之数组扁平化「赶紧收藏」

一、什么是数组扁平化

数组扁平化,就是将一个复杂的嵌套多层的数组,一层一层的转化为层级较少或者只有一层的数组。
比如:

// 多维数组
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:

  1. 数组连接用concat,保证数组的原有维度不变;
  2. push会改变原始数据,并且没有铺平数组的功能,你传什么进去,就是什么。会导致数组的维度发生变化,增加了数组的维度;
  3. ...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() 方法。

原理:toStringObject原型链上的一个方法,由于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的合法数据类型。


小结

数组扁平化的方法是有很多种的,但是大致也可以分为三类:

  1. ES6 的 falt():他的性能是最好的,但是需要考虑浏览器兼容性的问题;
  2. 通过 for() while() reduce() 等递归遍历的方式;
  3. 使用toString JSON 等转为字符串的方式,去除括号,最终再转为数组的方式;

以上所有的方法demo都可使用,若有其他更好的方式,欢迎大佬补充,若文中有不对的地方,还请指正,及时改进,共同进步。

希望上面的内容对你的工作学习有所帮助!欢迎各位一键三连哦~

各位 加油!

原创不易,还希望各位大佬支持一下 \textcolor{blue}{原创不易,还希望各位大佬支持一下} 原创不易,还希望各位大佬支持一下

👍 点赞,你的认可是我创作的动力! \textcolor{green}{点赞,你的认可是我创作的动力!} 点赞,你的认可是我创作的动力!

⭐️ 收藏,你的青睐是我努力的方向! \textcolor{green}{收藏,你的青睐是我努力的方向!} 收藏,你的青睐是我努力的方向!

✏️ 评论,你的意见是我进步的财富! \textcolor{green}{评论,你的意见是我进步的财富!} 评论,你的意见是我进步的财富!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值