面試官:“請講一講數組扁平化”!

扁平化

js當中涉及到扁平化這個概念的就是數組扁平化,第一次接觸到這個知識點的時候是在秋招的時候參加快手的面試, 當時想到的唯一辦法就是遞歸解決,如今再重新看這個問題又想到很多其他的解決方案,那麼今天就正式介紹一下什麼是數組扁平化。

面試官:“請講一講數組扁平化”!

數組的扁平化,就是將一個嵌套多層的數組 array (嵌套可以是任何層數)轉換為只有一層的數組。舉個例子,假設有個名為 flatten 的函數可以做到數組扁平化, 效果就會如下:

var arr = [1, [2, [3, 4]]];
console.log(flatten(arr)) // [1, 2, 3, 4]

接下來我們來編寫自己的flattern函數了。

遞歸

這也是我當時面試想到最簡單的解決方案,思路很簡單,通過遍歷最外層數組的每一個元素,看看是否還是數組,如果是的話,繼續遞歸執行,不是的話,放到最後的結果數組當中,代碼如下:

 let arr = [1, [2, [3, 4]]];
function flattern(arr) {
let result = [];
for(let i = 0; i < arr.length; i++) {
if(Array.isArray(arr[i])) {
flattern(arr[i])
} else {
result.push(arr[i])
}
}
return result;
}
console.log(flattern(arr));
toString
toString() 方法返回一個表示該對象的字符串。 ---MDN

這是mdn官方文檔對這個方法的解釋,講到這裡就不得不提到拆箱操作,在 JavaScript 標準中,規定了 ToPrimitive 函數,它是對象類型到基本類型的轉換(即, 拆箱轉換)。

對象到 String 和 Number 的轉換都遵循“先拆箱再轉換”的規則。通過拆箱轉換,把對象變成基本類型,再從基本類型轉換為對應的 String 或者 Number。

拆箱轉換會嘗試調用 valueOf 和 toString 來獲得拆箱後的基本類型。如果 valueOf 和 toString 都不存在,或者沒有返回基本類型,則會產生類型錯誤 TypeError。

面試官:“請講一講數組扁平化”!

這裡嵌套數組調用這個方法之後,直接是扁平化後的字符串,然後在調用split方法轉換成數組即可,代碼如下:

let arr = [1, [2, [3, 4]]];
function flatten(arr) {
return arr.toString().split(',').map(function(item){
return +item //+可以快速獲得Number類型
})
}
console.log(flatten(arr))

Tips:我們也可以通過toString() 來獲取每個對象的類型。為了每個對象都能通過 Object.prototype.toString() 來檢測,需要以 Function.prototype.call() 或者 Function.prototype.apply() 的形式來調用,傳遞要檢查的對象作為第一個參數,稱為thisArg。代碼如下:

let toString = Object.prototype.toString;
toString.call(new Date); // [object Date]
toString.call(new String); // [object String]
toString.call(Math); // [object Math]
//Since JavaScript 1.8.5
toString.call(undefined); // [object Undefined]
toString.call(null); // [object Null]

reduce

提到數組的方法,就會聯想到非常常用的2個高階函數map和reduce,這裡的扁平化操作我們也可以用reduce來實現,代碼如下:

let arr = [1, [2, [3, 4]]];
function flatten(arr) {
return arr.reduce(function(prev, next){
return prev.concat(Array.isArray(next) ? flatten(next) : next)
}, [])

}
console.log(flatten(arr))
...

ES6 增加了擴展運算符,用於取出參數對象的所有可遍歷屬性,拷貝到當前對象之中,所以也可以遞歸扁平化數組,代碼如下:

let arr = [1, [2, [3, 4]]];
function flatten(arr) {
while (arr.some(item => Array.isArray(item))) {
arr = [].concat(...arr);
} //ES6新方法
return arr;
}
console.log(flatten(arr))

undercore

那麼如何寫一個抽象的扁平函數,來方便我們的開發呢,所有又到了我們抄襲 underscore 的時候了~ 在這裡直接給出源碼和註釋,但是要注意,這裡的 flatten 函數並不是最終的 _.flatten,為了方便多個 API 進行調用,這裡對扁平進行了更多的配置,代碼如下:

/**
* 數組扁平化
* @param {Array} input 要處理的數組
* @param {boolean} shallow 是否只扁平一層
* @param {boolean} strict 是否嚴格處理元素,下面有解釋
* @param {Array} output 這是為了方便遞歸而傳遞的參數
* 源碼地址:https://github.com/jashkenas/underscore/blob/master/underscore.js#L528
*/
function flatten(input, shallow, strict, output) {

// 遞歸使用的時候會用到output
output = output || [];
var idx = output.length;
for (var i = 0, len = input.length; i < len; i++) {
var value = input[i];
// 如果是數組,就進行處理
if (Array.isArray(value)) {
// 如果是隻扁平一層,遍歷該數組,依此填入 output
if (shallow) {
var j = 0, len = value.length;
while (j < len) output[idx++] = value[j++];
}
// 如果是全部扁平就遞歸,傳入已經處理的 output,遞歸中接著處理 output
else {
flatten(value, shallow, strict, output);
idx = output.length;
}
}
// 不是數組,根據 strict 的值判斷是跳過不處理還是放入 output
else if (!strict){
output[idx++] = value;
}
}
return output;
}

如果覺得本文對你有所幫助,不如點個在看再走。

面試官:“請講一講數組扁平化”!


分享到:


相關文章: