精學(xué)手撕系列——數(shù)組扁平化
今天就帶大家從0了解flat特性到手寫實(shí)現(xiàn)flat,再到接住面試官的連環(huán)追問中重新學(xué)習(xí)一遍數(shù)組扁平化flat方法
Array.prototype.flat()
一段代碼總結(jié)Array.prototype.flat()特性
注:數(shù)組拍平方法 Array.prototype.flat() 也叫數(shù)組扁平化、數(shù)組拉平、數(shù)組降維。
- let arr = [12, 23, [34, 56, [78, 90, 100, [110, 120, 130]]]];
- console.log(arr.flat());
- // [ 12, 23, 34, 56, [ 78, 90, 100, [ 110, 120, 130 ] ] ]
- console.log(arr.flat(2));
- // [ 12, 23, 34, 56, 78, 90, 100, [ 110, 120, 130 ] ]
- console.log(arr.flat(Infinity));
- // [12, 23, 34, 56, 78, 90, 100, 110, 120, 130]
- console.log(arr.flat(0));
- // [12, 23, [34, 56, [78, 90, 100, [110, 120, 130]]]];
- console.log(arr.flat(-10));
- // [12, 23, [34, 56, [78, 90, 100, [110, 120, 130]]]];
- let arr2 = [12, 23, [34, 56, ,]]
- console.log(arr.flat());
- // [ 12, 23, 34, 56 ]
Array.prototype.flat() 特性總結(jié)
Array.prototype.flat() 用于將嵌套的數(shù)組“拉平”,變成一維的數(shù)組。該方法返回一個(gè)新數(shù)組,對(duì)原數(shù)據(jù)沒有影響。
不傳參數(shù)時(shí),默認(rèn)“拉平”一層,可以傳入一個(gè)整數(shù),表示想要“拉平”的層數(shù)。
傳入 <=0 的整數(shù)將返回原數(shù)組,不“拉平”
Infinity 關(guān)鍵字作為參數(shù)時(shí),無論多少層嵌套,都會(huì)轉(zhuǎn)為一維數(shù)組
如果原數(shù)組有空位,Array.prototype.flat() 會(huì)跳過空位。
面試官 N 連問:
第一問:下面數(shù)組如何實(shí)現(xiàn)扁平化?
- let arr = [
- [1, 2, 2],
- [3, 4, 5, 5],
- [6, 7, 8, 9, [11, 12, [12, 13, [14]]]], 10
- ];
小伙伴首先想到的肯定是用 ES6 的Array.prototype.flat方法呀
方法一:flat
- arr = arr.flat(2);
- // [ 1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 11, 12, [ 12, 13, [ 14 ] ], 10 ]
當(dāng)flat中傳入數(shù)字時(shí),是扁平對(duì)應(yīng)的層數(shù),顯然這不是我們想要的,因?yàn)樗€沒有完全展開。
這是,flat函數(shù)中就為我們提供了一個(gè)參數(shù)Infinity,譯為無窮的意思。
- arr = arr.flat(Infinity);
- /* [
- 1, 2, 2, 3, 4, 5, 5,
- 6, 7, 8, 9, 11, 12, 12,
- 13, 14, 10
- ] */
當(dāng)我們不知道數(shù)組中嵌套了幾維數(shù)組時(shí),我們可以用Infinity這個(gè)參數(shù),幫我們?nèi)空归_。
第二問:還有其它的辦法嗎?因?yàn)樗鼈冊(cè)诟甙姹緸g覽器并不兼容
方法二:轉(zhuǎn)換為字符串,再把字符串對(duì)象用,轉(zhuǎn)換成數(shù)組
可以先把多維數(shù)組先轉(zhuǎn)換為字符串,再基于,分隔符將字符串對(duì)象分割成字符串?dāng)?shù)組
toString() 扁平化數(shù)組
- arr = arr.toString();
- // "1,2,2,3,4,5,5,6,7,8,9,11,12,12,13,14,10"
- arr = arr.toString().split(',');
- // ["1", "2", "2", "3", "4", "5", "5", "6", "7", "8", "9", "11", "12", "12", "13", "14", "10"]
- arr = arr.toString().split(',').map(item => parseFloat(item));
- // [1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 11, 12, 12, 13, 14, 10]
除了上面的方法還有什么方法轉(zhuǎn)換為字符串呢?
JSON.stringify()扁平化數(shù)組
- arr = JSON.stringify(arr);
- // "[[1,2,2],[3,4,5,5],[6,7,8,9,[11,12,[12,13,[14]]]],10]"
- arr = JSON.stringify(arr).replace(/(\[|\])/g, '');
- // "1,2,2,3,4,5,5,6,7,8,9,11,12,12,13,14,10"
- arr = JSON.stringify(arr).replace(/(\[|\])/g, '').split(',').map(item=>parseFloat(item));
- // [1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 11, 12, 12, 13, 14, 10]
方法三:循環(huán)驗(yàn)證是否為數(shù)組
基于數(shù)組的some方法,只要數(shù)組里面有一項(xiàng)元素是數(shù)組就繼續(xù)循環(huán),扁平數(shù)組
核心:[].concat(...arr)
- whilte (arr.some(item => Array.isArray(item))) {
- arr = [].concat(...arr);
- }
- console.log(arr); // [1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 11, 12, 12, 13, 14, 10]
第三問:能自己實(shí)現(xiàn)一個(gè) flat 扁平化嗎?
先回答:能??!再跟著我分析思路:
如何實(shí)現(xiàn)呢,其實(shí)思路非常簡(jiǎn)單:在數(shù)組中找到是數(shù)組類型的元素,然后將他們展開,這就是flat方法的關(guān)鍵思路
實(shí)現(xiàn)思路:
循環(huán)數(shù)組里的每一個(gè)元素
判斷該元素是否為數(shù)組
是數(shù)組的話,繼續(xù)循環(huán)遍歷這個(gè)元素——數(shù)組
不是數(shù)組的話,把元素添加到新的數(shù)組中
實(shí)現(xiàn)流程:
創(chuàng)建一個(gè)空數(shù)組,用來保存遍歷到的非數(shù)組元素
創(chuàng)建一個(gè)循環(huán)遍歷數(shù)組的函數(shù),cycleArray
取得數(shù)組中的每一項(xiàng),驗(yàn)證Array.isArray()
數(shù)組的話,繼續(xù)循環(huán)
非數(shù)組的話,添加到新數(shù)組中
返回新數(shù)組對(duì)象
ES5 實(shí)現(xiàn) flat 扁平化方法
- let arr = [
- [1, 2, 2],
- [3, 4, 5, 5],
- [6, 7, 8, 9, [11, 12, [12, 13, [14]]]], 10
- ];
- function myFlat() {
- _this = this; // 保存 this:arr
- let newArr = [];
- // 循環(huán)arr中的每一項(xiàng),把不是數(shù)組的元素存儲(chǔ)到 newArr中
- let cycleArray = (arr) => {
- for (let i=0; i< arr.length; i++) {
- let item = arr[i];
- if (Array.isArray(item)) { // 元素是數(shù)組的話,繼續(xù)循環(huán)遍歷該數(shù)組
- cycleArray(item);
- continue;
- } else{
- newArr.push(item); // 不是數(shù)組的話,直接添加到新數(shù)組中
- }
- }
- }
- cycleArray(_this); // 循環(huán)數(shù)組里的每個(gè)元素
- return newArr; // 返回新的數(shù)組對(duì)象
- }
- Array.prototype.myFlat = myFlat;
- arr = arr.myFlat(); // [1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 11, 12, 12, 13, 14, 10]
ES6 實(shí)現(xiàn) flat 扁平化方法
- const myFlat = (arr) => {
- let newArr = [];
- let cycleArray = (arr) => {
- for(let i = 0; i < arr.length; i++) {
- let item = arr[i];
- if (Array.isArray(item)) {
- cycleArray(item);
- continue;
- } else {
- newArr.push(item);
- }
- }
- }
- cycleArray(arr);
- return newArr;
- }
- myFlat(arr); // [1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 11, 12, 12, 13, 14, 10]
第四問:請(qǐng)使用reduce實(shí)現(xiàn)flat函數(shù)
相信很多面試官都會(huì)指定讓面試者用reduce方法實(shí)現(xiàn)flat函數(shù)
其實(shí)思路也是一樣的,在實(shí)現(xiàn)之前,先來看一下
它的核心:[].concat(...arr)
但是它只能將數(shù)組元素展開一層,來看下面例子:
- let arr2 = [12, 23, [34, 56, [78, 90, 100]]];
- [].concat(...arr2);
- // [ 12, 23, 34, 56, [ 78, 90, 100 ] ]
細(xì)心的同學(xué)可以發(fā)現(xiàn)[].concat(...arr)只能展開一層數(shù)組元素,當(dāng)有更深層次的,是無法展開的
接下來,我們來看看用reduce怎么實(shí)現(xiàn)?
- let arr = [12, 23, [34, 56, [78, 90, 100, [110, 120, 130, 140]]]];
- const myFlat = arr => {
- return arr.reduce((pre, cur) => {
- return pre.concat(cur);
- }, []);
- };
- console.log(myFlat(arr));
- // [ 12, 23, 34, 56, [ 78, 90, 100, [ 110, 120, 130, 140 ] ] ]
- const myFlat = arr => {
- return arr.reduce((pre, cur) => {
- return pre.concat(Array.isArray(cur) ? myFlat(cur) : cur);
- }, []);
- };
- console.log(myFlat(arr));
- // [12, 23, 34, 56, 78, 90, 100, 110, 120, 130, 140]
上面代碼中的Array.isArray(cur)和myFlat(cur)實(shí)際就好比與遍歷數(shù)組每一項(xiàng),看它是不是數(shù)組元素,
如果是的話,則繼續(xù)遞歸遍歷,不是的話直接數(shù)組合并非數(shù)組元素。
第五問:使用棧的思想實(shí)現(xiàn)flat函數(shù)
棧思想: 后進(jìn)先出的數(shù)據(jù)結(jié)構(gòu)
實(shí)現(xiàn)思路:
不斷獲取并刪除棧中最后一個(gè)元素A,判斷A是否為數(shù)組元素,直到棧內(nèi)元素為空,全部添加到newArr
是數(shù)組,則push到棧中,繼續(xù)循環(huán)棧內(nèi)元素,直到棧為空
不是數(shù)組,則unshift添加到newArr中
- // 棧思想
- function flat(arr) {
- const newArr = [];
- const stack = [].concat(arr); // 將數(shù)組元素拷貝至棧,直接賦值會(huì)改變?cè)瓟?shù)組
- //如果棧不為空,則循環(huán)遍歷
- while (stack.length !== 0) {
- const val = stack.pop(); // 刪除數(shù)組最后一個(gè)元素,并獲取它
- if (Array.isArray(val)) {
- stack.push(...val); // 如果是數(shù)組再次入棧,并且展開了一層
- } else {
- newArr.unshift(val); // 如果不是數(shù)組就將其取出來放入結(jié)果數(shù)組中
- }
- }
- return newArr;
- }
- let arr = [12, 23, [34, 56, [78, 90, 100, [110, 120, 130, 140]]]];
- console.log(flat(arr));
- // [12, 23, 34, 56, 78, 90, 100, 110, 120, 130, 140]
本文總結(jié)
看完這篇文章的同學(xué),可以在面試的時(shí)候分類,分思想給面試官描述,可以先說我用哪幾種思想實(shí)現(xiàn)過,它們的寫法又分別有什么不同。