精学手撕系列——数组扁平化

开发
在前端面试中,手写flat是非常基础的面试题,通常出现在笔试或者第一轮面试中,主要考察面试者基本的手写代码能力和JavaScript的基本功。

 今天就带大家从0了解flat特性到手写实现flat,再到接住面试官的连环追问中重新学习一遍数组扁平化flat方法

[[342383]]

Array.prototype.flat()
一段代码总结Array.prototype.flat()特性

注:数组拍平方法 Array.prototype.flat() 也叫数组扁平化、数组拉平、数组降维。

  1. let arr = [12, 23, [34, 56, [78, 90, 100, [110, 120, 130]]]]; 
  2.  
  3. console.log(arr.flat());  
  4. // [ 12, 23, 34, 56, [ 78, 90, 100, [ 110, 120, 130 ] ] ] 
  5.  
  6. console.log(arr.flat(2)); 
  7. // [ 12, 23, 34, 56, 78, 90, 100, [ 110, 120, 130 ] ] 
  8.  
  9. console.log(arr.flat(Infinity)); 
  10. // [12, 23, 34, 56, 78, 90, 100, 110, 120, 130] 
  11.  
  12. console.log(arr.flat(0)); 
  13. // [12, 23, [34, 56, [78, 90, 100, [110, 120, 130]]]]; 
  14.  
  15. console.log(arr.flat(-10)); 
  16. // [12, 23, [34, 56, [78, 90, 100, [110, 120, 130]]]]; 
  17.  
  18. let arr2 = [12, 23, [34, 56, ,]] 
  19. console.log(arr.flat()); 
  20. // [ 12, 23, 34, 56 ] 

Array.prototype.flat() 特性总结

Array.prototype.flat() 用于将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组,对原数据没有影响。

不传参数时,默认“拉平”一层,可以传入一个整数,表示想要“拉平”的层数。

传入 <=0 的整数将返回原数组,不“拉平”

Infinity 关键字作为参数时,无论多少层嵌套,都会转为一维数组

如果原数组有空位,Array.prototype.flat() 会跳过空位。

面试官 N 连问:
第一问:下面数组如何实现扁平化?

  1. let arr = [ 
  2.   [1, 2, 2], 
  3.   [3, 4, 5, 5], 
  4.   [6, 7, 8, 9, [11, 12, [12, 13, [14]]]], 10 
  5. ]; 

小伙伴首先想到的肯定是用 ES6 的Array.prototype.flat方法呀

方法一:flat

  1. arr = arr.flat(2); 
  2. // [ 1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 11, 12, [ 12, 13, [ 14 ] ], 10 ] 

当flat中传入数字时,是扁平对应的层数,显然这不是我们想要的,因为它还没有完全展开。

这是,flat函数中就为我们提供了一个参数Infinity,译为无穷的意思。

  1. arr = arr.flat(Infinity); 
  2. /* [ 
  3.    1,  2,  2, 3,  4,  5,  5, 
  4.    6,  7,  8, 9, 11, 12, 12, 
  5.   13, 14, 10 
  6. ] */ 

当我们不知道数组中嵌套了几维数组时,我们可以用Infinity这个参数,帮我们全部展开。

第二问:还有其它的办法吗?因为它们在高版本浏览器并不兼容
方法二:转换为字符串,再把字符串对象用,转换成数组
可以先把多维数组先转换为字符串,再基于,分隔符将字符串对象分割成字符串数组

toString() 扁平化数组

  1. arr = arr.toString(); 
  2. // "1,2,2,3,4,5,5,6,7,8,9,11,12,12,13,14,10" 
  3.  
  4. arr = arr.toString().split(','); 
  5. // ["1""2""2""3""4""5""5""6""7""8""9""11""12""12""13""14""10"
  6.  
  7. arr = arr.toString().split(',').map(item => parseFloat(item)); 
  8. // [1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 11, 12, 12, 13, 14, 10] 

除了上面的方法还有什么方法转换为字符串呢?

JSON.stringify()扁平化数组

  1. arr = JSON.stringify(arr); 
  2. // "[[1,2,2],[3,4,5,5],[6,7,8,9,[11,12,[12,13,[14]]]],10]" 
  3.  
  4. arr = JSON.stringify(arr).replace(/(\[|\])/g, ''); 
  5. // "1,2,2,3,4,5,5,6,7,8,9,11,12,12,13,14,10" 
  6.  
  7. arr = JSON.stringify(arr).replace(/(\[|\])/g, '').split(',').map(item=>parseFloat(item)); 
  8. // [1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 11, 12, 12, 13, 14, 10] 

方法三:循环验证是否为数组
基于数组的some方法,只要数组里面有一项元素是数组就继续循环,扁平数组

核心:[].concat(...arr)

  1. whilte (arr.some(item => Array.isArray(item))) { 
  2.  arr = [].concat(...arr); 
  3.  
  4. console.log(arr); // [1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 11, 12, 12, 13, 14, 10] 

第三问:能自己实现一个 flat 扁平化吗?
先回答:能!!再跟着我分析思路:

如何实现呢,其实思路非常简单:在数组中找到是数组类型的元素,然后将他们展开,这就是flat方法的关键思路

实现思路:

循环数组里的每一个元素
判断该元素是否为数组
是数组的话,继续循环遍历这个元素——数组
不是数组的话,把元素添加到新的数组中
实现流程:

创建一个空数组,用来保存遍历到的非数组元素
创建一个循环遍历数组的函数,cycleArray
取得数组中的每一项,验证Array.isArray()
数组的话,继续循环
非数组的话,添加到新数组中
返回新数组对象
ES5 实现 flat 扁平化方法

  1. let arr = [ 
  2.     [1, 2, 2], 
  3.     [3, 4, 5, 5], 
  4.     [6, 7, 8, 9, [11, 12, [12, 13, [14]]]], 10 
  5. ]; 
  6.  
  7. function myFlat() { 
  8.   _this = this; // 保存 this:arr 
  9.   let newArr = []; 
  10.   // 循环arr中的每一项,把不是数组的元素存储到 newArr中 
  11.   let cycleArray = (arr) => { 
  12.     for (let i=0; i< arr.length; i++) { 
  13.       let item = arr[i]; 
  14.       if (Array.isArray(item)) { // 元素是数组的话,继续循环遍历该数组 
  15.         cycleArray(item); 
  16.         continue
  17.       } else
  18.         newArr.push(item); // 不是数组的话,直接添加到新数组中 
  19.       } 
  20.     } 
  21.   } 
  22.   cycleArray(_this); // 循环数组里的每个元素 
  23.   return newArr; // 返回新的数组对象 
  24.  
  25. Array.prototype.myFlat = myFlat; 
  26.  
  27. arr = arr.myFlat(); // [1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 11, 12, 12, 13, 14, 10] 

ES6 实现 flat 扁平化方法

  1. const myFlat = (arr) => { 
  2.   let newArr = []; 
  3.   let cycleArray = (arr) => { 
  4.     for(let i = 0; i < arr.length; i++) { 
  5.       let item = arr[i]; 
  6.       if (Array.isArray(item)) { 
  7.         cycleArray(item); 
  8.         continue
  9.       } else { 
  10.         newArr.push(item); 
  11.       } 
  12.     } 
  13.   } 
  14.   cycleArray(arr); 
  15.   return newArr; 
  16.  
  17. myFlat(arr); // [1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 11, 12, 12, 13, 14, 10] 

第四问:请使用reduce实现flat函数
相信很多面试官都会指定让面试者用reduce方法实现flat函数

其实思路也是一样的,在实现之前,先来看一下

它的核心:[].concat(...arr)

但是它只能将数组元素展开一层,来看下面例子:

  1. let arr2 = [12, 23, [34, 56, [78, 90, 100]]]; 
  2. [].concat(...arr2); 
  3. // [ 12, 23, 34, 56, [ 78, 90, 100 ] ] 

细心的同学可以发现[].concat(...arr)只能展开一层数组元素,当有更深层次的,是无法展开的

接下来,我们来看看用reduce怎么实现?

  1. let arr = [12, 23, [34, 56, [78, 90, 100, [110, 120, 130, 140]]]]; 
  2. const myFlat = arr => { 
  3.   return arr.reduce((pre, cur) => { 
  4.     return pre.concat(cur); 
  5.   }, []); 
  6. }; 
  7. console.log(myFlat(arr)); 
  8. // [ 12, 23, 34, 56, [ 78, 90, 100, [ 110, 120, 130, 140 ] ] ] 
  9.  
  10. const myFlat = arr => { 
  11.   return arr.reduce((pre, cur) => { 
  12.     return pre.concat(Array.isArray(cur) ? myFlat(cur) : cur); 
  13.   }, []); 
  14. }; 
  15. console.log(myFlat(arr)); 
  16. // [12, 23, 34, 56, 78, 90, 100, 110, 120, 130, 140] 

上面代码中的Array.isArray(cur)和myFlat(cur)实际就好比与遍历数组每一项,看它是不是数组元素,

如果是的话,则继续递归遍历,不是的话直接数组合并非数组元素。

第五问:使用栈的思想实现flat函数
栈思想: 后进先出的数据结构

实现思路:

不断获取并删除栈中最后一个元素A,判断A是否为数组元素,直到栈内元素为空,全部添加到newArr

是数组,则push到栈中,继续循环栈内元素,直到栈为空
不是数组,则unshift添加到newArr中

  1. // 栈思想 
  2. function flat(arr) { 
  3.   const newArr = []; 
  4.   const stack = [].concat(arr);  // 将数组元素拷贝至栈,直接赋值会改变原数组 
  5.   //如果栈不为空,则循环遍历 
  6.   while (stack.length !== 0) { 
  7.     const val = stack.pop(); // 删除数组最后一个元素,并获取它 
  8.     if (Array.isArray(val)) { 
  9.       stack.push(...val); // 如果是数组再次入栈,并且展开了一层 
  10.     } else { 
  11.       newArr.unshift(val); // 如果不是数组就将其取出来放入结果数组中 
  12.     } 
  13.   } 
  14.   return newArr; 
  15.  
  16. let arr = [12, 23, [34, 56, [78, 90, 100, [110, 120, 130, 140]]]]; 
  17. console.log(flat(arr)); 
  18. // [12, 23, 34, 56, 78, 90, 100, 110, 120, 130, 140] 

本文总结
看完这篇文章的同学,可以在面试的时候分类,分思想给面试官描述,可以先说我用哪几种思想实现过,它们的写法又分别有什么不同。

 

责任编辑:姜华 来源: 前端时光屋
相关推荐

2020-09-17 14:04:32

拷贝

2013-09-22 15:03:27

扁平化UI设计

2013-09-22 15:43:27

扁平化UI设计

2013-09-22 14:47:50

扁平化UI设计

2013-09-22 14:56:26

扁平化UI设计

2013-09-22 14:30:43

扁平化设计

2013-09-22 16:36:07

扁平化UI设计

2013-09-22 16:17:59

扁平化UI设计

2013-09-22 16:22:58

扁平化UI设计

2019-04-08 16:29:28

多云扁平化云计算

2013-09-23 10:16:04

扁平化UI设计

2013-07-11 09:45:48

扁平化扁平化设计

2013-09-25 10:52:27

Android 4.4扁平化

2013-09-22 16:30:13

扁平化UI设计

2021-11-07 14:37:59

列表编码扁平化

2013-06-09 11:04:07

设计扁平化设计平面化设计

2013-01-28 09:39:05

用户界面扁平化设计

2021-11-24 08:43:02

扁平化函数数组

2012-02-20 14:48:30

网络扁平化

2010-10-25 14:16:59

扁平化网络
点赞
收藏

51CTO技术栈公众号