JS前端面試數(shù)組扁平化手寫flat函數(shù)示例
前言
由于上半年參加了校招的技術(shù)面試, 前前后后面了20多個(gè)人了, 每次面試都會(huì)讓應(yīng)聘者手寫一下數(shù)組扁平化flat()
,但是發(fā)現(xiàn)居然沒有一個(gè)能夠完成寫出來, 所以打算總結(jié)一下如果遇到了數(shù)組扁平化的題目(也可以叫做手動(dòng)封裝flat()
方法),到底應(yīng)該怎么寫,怎么寫可以得更高的分;
話不多說, 接下來我將站在面試官的角度分析所有常見的實(shí)現(xiàn)方法, 并附上對應(yīng)的得分情況: 五星打分制
滿分: ?????
題目 實(shí)現(xiàn)扁平化的方法 封裝 flatten
題目描述:
已有多級(jí)嵌套數(shù)組 : [1, [2, [3, [4, 5]]], 6]
將其扁平化處理 輸出: [1,2,3,4,5,6]
什么是扁平化
定義 : 扁平化就是將多維數(shù)組變成一維數(shù)組,不存在數(shù)組的嵌套
1. ES6 flat
flat(depth) 方法會(huì)按照一個(gè)可指定的深度遞歸遍歷數(shù)組,并將所有元素與遍歷到的子數(shù)組中的元素合并為一個(gè)新數(shù)組返回。
參數(shù):
depth
(可選) 指定要提取嵌套數(shù)組的結(jié)構(gòu)深度,默認(rèn)值為 1
返回值:
返回一個(gè)新數(shù)組,包含數(shù)組與提取嵌套數(shù)組的所有元素的新數(shù)組
使用 Infinity
,可展開任意深度的嵌套數(shù)組
封裝思路: 使用 es6 自帶 API 處理
const arr = [1, [2, [3, [4, 5]]], 6] function flatten(params) { return params.flat(Infinity) } console.log(flatten(arr)); // 輸出: [1,2,3,4,5,6]
得分 : ?
直接使用自帶的方法可以很快的實(shí)現(xiàn), 但是面試官當(dāng)然不希望就看到這些呀 !
2. toString
如果數(shù)組的項(xiàng)全為數(shù)字,可以使用join(),toString()可以利用數(shù)組toString() 轉(zhuǎn)為字符串
function flatten(arr) { return arr.toString().split(',').map(item =>parseFloat(item)) } console.log(flatten(arr)); // 輸出:[ 1, 2, 3, 4, 5, 6 ]
得分 : ? (并不是要考你的數(shù)組的方法調(diào)用)
3. 使用正則替換
看到嵌套的數(shù)組,如果在字符串的角度上看就是多了很多[
和]
,如果把它們替換就可以實(shí)現(xiàn)簡單的扁平化
function flatten (arr) { console.log('JSON.stringify(arr)', typeof JSON.stringify(arr)) let str= JSON.stringify(arr).replace(/(\[|\])/g, ''); str = '[' + str + ']'; arr = JSON.parse(str); return arr } console.log(flatten(arr))
得分 : ? (并不是要考你的數(shù)組的方法調(diào)用)
4. 循環(huán)遞歸
4.1 循環(huán) + concat + push
當(dāng)只有一層嵌套數(shù)組使用push的方式扁平化
[1, [2, 3,4,5,6]]
let result = []; for (let i = 0; i < arr2.length; i++) { result = result.concat((arr2[i])); } console.log(result); [ 1, 2, 3, 4, 5, 6 ]
如果有多層嵌套的數(shù)組就需要使用 遞歸的思想 :
思路
- 循環(huán)判斷數(shù)組的每一項(xiàng)是否是數(shù)組: Array.isArray(arr[i])
- 是數(shù)組就遞歸調(diào)用上面的扁平化一層的代碼 result = result.concat(flatten(arr[i]));
- 不是數(shù)組,直接通過push添加到返回值數(shù)組
function flatten(arr) { let result = []; for (let i = 0; i < arr.length; i++) { if (Array.isArray(arr[i])) { result = result.concat(flatten(arr[i])); } else { result.push(arr[i]) } } return result } console.log(flatten(arr));
或者使用forEach
立即執(zhí)行函數(shù)
// 遞歸版本的反嵌套 function flatten(array) { var flattend = []; (function flat(array) { array.forEach(function(el) { if (Array.isArray(el)) flat(el); else flattend.push(el); }); })(array); return flattend; }
當(dāng)然循環(huán)可以更改成forEach循環(huán),for of ...等其他循環(huán),簡單的循環(huán)遞歸就能夠一樣的解決啦~
得分: ??? (能夠使用遞歸寫出數(shù)組扁平化,缺少控制層級(jí)關(guān)系)
4.2 增加參數(shù)控制扁平化深度
這個(gè)可以理解為手寫flat()
方法啦~
// forEach 遍歷數(shù)組會(huì)自動(dòng)跳過空元素 const eachFlat = (arr = [], depth = 1) => { const result = []; // 緩存遞歸結(jié)果 // 開始遞歸 (function flat(arr, depth) { // forEach 會(huì)自動(dòng)去除數(shù)組空位 arr.forEach((item) => { // 控制遞歸深度 if (Array.isArray(item) && depth > 0) { // 遞歸數(shù)組 flat(item, depth - 1) } else { // 緩存元素 result.push(item) } }) })(arr, depth) // 返回遞歸結(jié)果 return result; } // for of 循環(huán)不能去除數(shù)組空位,需要手動(dòng)去除 const forFlat = (arr = [], depth = 1) => { const result = []; (function flat(arr, depth) { for (let item of arr) { if (Array.isArray(item) && depth > 0) { flat(item, depth - 1) } else { // 去除空元素,添加非 undefined 元素 item !== void 0 && result.push(item); } } })(arr, depth) return result; }
得分: ???? (能夠使用遞歸寫出數(shù)組扁平化,可以通過參數(shù)控制層級(jí)關(guān)系)
4.3 巧用 reduce
reduce 方法為數(shù)組中的每個(gè)元素按序執(zhí)行一個(gè)reducer
函數(shù),每一次運(yùn)行 reducer 會(huì)將先前元素的計(jì)算結(jié)構(gòu)作為參數(shù)傳入,最后將其結(jié)果匯總為單個(gè)返回值
參數(shù):
callbackFn
一個(gè) reducer 函數(shù),包含四個(gè)參數(shù):
- previousVal :上一次調(diào)用
callbackFn
時(shí)的返回值,在第一次調(diào)用時(shí),若指定了初始值initialValue
,previousVal 的值就位 initialValue,否則初始值就是為數(shù)組的索引為 0 的元素 currentVal
:數(shù)組中正在處理的元素,在第一次調(diào)用時(shí),若指定了初始值,其值則為數(shù)組索引為 0 的元素 array[0],否則為 array[1]- currentIndex: 數(shù)組中正在處理的元素的索引,若指定了初始值 initialValue,則起始索引號(hào)為 0,否則從索引 1 起始
- array : 用于遍歷的數(shù)組
initialValue
(可選) : 作為第一次調(diào)用 callback 函數(shù)時(shí)參數(shù) previousValue 的值
返回值: 使用 reducer 回調(diào)函數(shù)遍歷整個(gè)數(shù)組后的結(jié)果
思路:
當(dāng)我們使用 reduce 來解析第一層數(shù)組,可以得到:
const arr = [1, [[2, 3], 4],5] const result = arr.reduce((acc, val) => acc.concat(val), []); console.log(result); // 輸出: [1,[2,3],4,5]
可以看出上面的代碼可以扁平化一層數(shù)組,對于多層級(jí)嵌套的數(shù)組, 這個(gè)時(shí)候就需要使用遞歸的思想來解決問題了,再次遍歷數(shù)組,發(fā)現(xiàn)數(shù)組元素任然是數(shù)組的時(shí)候,再次執(zhí)行上面扁平化
手寫方法
const arr = [1, [[2, 3], 4],5] function flatten (arr,deep=1) { return arr.reduce((acc,val) => acc.concat(Array.isArray(val)? flatten(val,deep-1):val),[]) } console.log(arr, Infinity); // 輸出:[ 1, 2, 3, 4, 5, 6 ]
得分: ???? (能夠使用遞歸寫出數(shù)組扁平化,巧用reduce方法可加分)
4.4 使用 Generator 函數(shù)
GeneratorFunction是協(xié)程在 ES6 的實(shí)現(xiàn),最大特點(diǎn)就是可以交出函數(shù)的執(zhí)行權(quán)(即暫停執(zhí)行)。
它不同于普通函數(shù),是可以暫停執(zhí)行的,所以函數(shù)名之前要加星號(hào),以示區(qū)別。
整個(gè) Generator 函數(shù)就是一個(gè)封裝的異步任務(wù),或者說是異步任務(wù)的容器。異步操作需要暫停的地方,都用 yield 語句注明。Generator 函數(shù)的執(zhí)行方法如下。
構(gòu)造器生成新的生成器函數(shù)
function* flatten(array) { for (const item of array) { if (Array.isArray(item)) { yield* flatten(item); } else { yield item; } } }
得分: ??? (使用Generator 函數(shù) 進(jìn)行遞歸)
5. 使用堆棧 stack 避免遞歸
遞歸循環(huán)都可通過維護(hù)一個(gè)堆結(jié)構(gòu)來解決
如果不使用遞歸數(shù)組來實(shí)現(xiàn)扁平化,可以使用堆棧來解決
深度的控制比較低效,因?yàn)樾枰獧z查每一個(gè)值的深度
思路:
- 把數(shù)組通過一個(gè)棧來維護(hù)
- 當(dāng)棧不為空的時(shí)候循環(huán)執(zhí)行處理
- pop()將棧尾出棧
- 如果出棧的元素是數(shù)組,就將該元素解構(gòu)后每一元素進(jìn)行入棧操作
- 出棧的元素不是數(shù)組就push進(jìn)返回值res
- 反轉(zhuǎn)恢復(fù)原數(shù)組的順序
var arr1 = [1,2,3,[1,2,3,4, [2,3,4]]]; function flatten(arr) { const stack = [...arr]; const res = []; while (stack.length) { // 使用 pop 從 stack 中取出并移除值 const next = stack.pop(); if (Array.isArray(next)) { // 使用 push 送回內(nèi)層數(shù)組中的元素,不會(huì)改動(dòng)原始輸入 stack.push(...next); } else { res.push(next); } } // 反轉(zhuǎn)恢復(fù)原數(shù)組的順序 return res.reverse(); } flatten(arr1);// [1, 2, 3, 1, 2, 3, 4, 2, 3, 4]
得分: ???? (使用數(shù)據(jù)結(jié)構(gòu)棧的特性取代遞歸操作,減少時(shí)間復(fù)雜度)
6.while 循環(huán)+ some方法
some :方法測試數(shù)組中是不是至少有 1 個(gè)元素通過了被提供的函數(shù)測試。它返回的是一個(gè) Boolean 類型的值。
思路
通過some來判斷數(shù)組中是否用數(shù)組,通過while不斷循環(huán)執(zhí)行判斷, 如果是數(shù)組的話可以使用 拓展運(yùn)算符... ... 每次只能展開最外層的數(shù)組,加上contact
來減少嵌套層數(shù),
function flatten(arr) { while (arr.some(item=> Array.isArray(item))) { console.log(...arr) arr = [].concat(...arr) console.log(arr) } return arr } console.log(flatten(arr));
得分: ???? (使用while循環(huán)取消遞歸操作, 巧用some操作進(jìn)行判斷)
以上就是JS前端面試數(shù)組扁平化手寫flat函數(shù)示例的詳細(xì)內(nèi)容,更多關(guān)于JS數(shù)組扁平化flat函數(shù)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
微信瀏覽器禁止頁面下拉查看網(wǎng)址實(shí)例詳解
這篇文章主要介紹了微信瀏覽器禁止頁面下拉查看網(wǎng)址實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-06-06uniapp封裝canvas組件無腦繪制保存小程序分享海報(bào)
這篇文章主要為大家介紹了uniapp封裝canvas組件和方法無腦繪制并保存小程序分享海報(bào)實(shí)現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08微信小程序 高德地圖SDK詳解及簡單實(shí)例(源碼下載)
這篇文章主要介紹了微信小程序 高德地圖詳解及簡單實(shí)例(源碼下載)的相關(guān)資料,需要的朋友可以參考下2017-01-01微信小程序 實(shí)現(xiàn)tabs選項(xiàng)卡效果實(shí)例代碼
這篇文章主要介紹了微信小程序 實(shí)現(xiàn)tabs選項(xiàng)卡效果實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2016-10-10微信小程序 詳解下拉加載與上拉刷新實(shí)現(xiàn)方法
這篇文章主要介紹了微信小程序 詳解下拉加載與上拉刷新實(shí)現(xiàn)方法的相關(guān)資料,這里介紹了兩種實(shí)現(xiàn)方法,需要的朋友可以參考下2017-01-01JS實(shí)現(xiàn)layui?table篩選框記憶功能
這篇文章主要介紹了JS實(shí)現(xiàn)layui?table篩選框記憶功能,本案例放入本地緩存的方式,使用MutationObserver實(shí)現(xiàn)監(jiān)控點(diǎn)擊事件,需要的朋友可以參考下2022-01-01