JavaScript專題之八:陣列扁平化

2021-03-11 10:01:19

目錄

  • 一、遞迴
  • 二、reduce
  • 三、apply+some
  • 四、ES6展開運運算元
  • 五、toString
  • 六、正則
  • 七、實現自己的扁平化工具方法
  • 寫在最後

(免費學習推薦:

一、遞迴

for迴圈是我們在進行陣列操作時最容易想到的,在不考慮時間和空間複雜度的時候,遞迴應該是一個完美的選擇!

範例:

輸入 
const arr = [1, [2, [3, 4, { a: 1 }], null], undefined];`
輸出 
[ 1, 2, 3, 4, { a: 1 }, null, undefined ]

程式碼:

function flatten(arr) {
    let res = [];
    for (let i = 0; i < arr.length; i++) {
        if (Array.isArray(arr[i])) {
            // 因為函數返回的是陣列,所以要做拼接處理
            res = res.concat(flatten(arr[i]));
        } else {
            res.push(arr[i])
        }
    }
    return res;}

注意:

  1. 判斷陣列內元素的基本型別
  2. 如果不是陣列:直接存入新陣列中
  3. 如果是陣列:重複第一步,直到將最後一個不是陣列的元素存入到新陣列中

二、reduce

先來看看reduce是做什麼的:reduce() 方法對陣列中的每個元素執行一個由您提供的reducer函數(升序執行),將其結果彙總為單個返回值。

範例:

輸入 
const arr = [1, [2, [3, 4, { a: 1 }], null], undefined];`
輸出 
[ 1, 2, 3, 4, { a: 1 }, null, undefined ]

程式碼:

function flatten(arr) {
    return arr.reduce((prev, next) => {
        // prev表示上一次操作的結果
        return prev.concat(Array.isArray(next) ? flatten(next) : next)
    }, [])
    // 注意reduce的初始值應該是[],否則無法進行拼接}

注意:

仔細對照方法一方法二,兩者思路完全一致——找到資料型別是陣列的子元素,對其進行抹平處理,只不過實現的細節略有不同。

唯一需要注意的地方就是拼接時資料的基本型別要以陣列開始。

三、apply+some

既然方法二是方法一的變式,那個方法三也可以說是方法二的變式,但不在採用遞迴的方式,而是一層一層「拆除」巢狀的方式

先來看看用到的API:

  • apply:呼叫一個具有給定this值的函數,以及以一個陣列(或類陣列物件)的形式提供的引數。
  • some:測試陣列中是不是至少有1個元素通過了被提供的函數測試。它返回的是一個Boolean型別的值

程式碼:

function flatten(arr) {
    while (arr.some(item => Array.isArray(item))) {
        // 只要存在陣列型別的元素,就抹平一層
        arr = [].concat.apply([], arr)
    }
    return arr;}

注意:

大家疑惑的點主要在apply,其實主要目的就是減少括號

let res = [];res  = res.concat({});// 等價於[].concat.apply([], [{}])

在本例中:

arr = [].concat.apply([], arr);// 等價於[].concat(1, [2, [3, 4, { a: 1 }], null], undefined)

四、ES6展開運運算元

大家可能業也注意到了,方法一到方法三 我們不斷的藉助現有方法,以精簡我們的程式碼量,本方法也是如此~

我們利用ES6的展開運運算元(用於取出引數物件的所有可遍歷屬性,拷貝到當前物件之中),繼續精簡方法三:

程式碼:

function flatten(arr) {
    while (arr.some(item => Array.isArray(item))) {
        // 只要存在陣列型別的元素,就抹平一層
        arr = [].concat(...arr)
    }
    return arr;}

五、toString

如果前面四個方法是我們可以實現的主流的方式,那麼接下來的幾個就是我們可以實現的「非主流」方式,直接上程式碼!

function flatten(arr) {
    return arr.toString().split(',').map(function(item){
        return +item    })}

注意:

將資料轉換型別存在型別的限制,如果原始資料是這樣的:[1, ‘2’],那麼就會出現問題

六、正則

假如我們預設了型別會被轉換這一缺陷,那麼還可以通過更暴力裡的方式來將陣列扁平化:

function flatten(arr) {
    return JSON.stringify(arr).replace(/\[|\]/g, '').split(',');}

注意:

如果是純陣列,貌似沒什麼問題

function flatten(arr) {
    return JSON.parse('[' + JSON.stringify(arr).replace(/\[|\]/g, '').split(',') + ']')}

但如果陣列記憶體在物件,就需要消除JSON後的後果,這樣更嚴謹一些~

到這裡,六種方法算是包括了大部分陣列扁平化的實現,那麼如果希望將我們的方法「升級」成工具怎麼辦?這時候我們就要「抄襲」一下loadsh了~

七、實現自己的扁平化工具方法

這裡我們簡化了loadsh裡的flatten函數,具體改動我們來看程式碼:

 /*
 * @private
 * @param {Array} array 需要扁平化的陣列
 * @param {number} depth 最多處理幾層
 * @param {boolean} [isStrict] 是否嚴格處理常式
 * @param {Array} [result=[]] 輸出的陣列
 * @returns {Array}
 */function flatten(array, depth, isStrict, result) {
    result || (result = [])
    // 邊界
    if (array == null) return result;

    for (const value of array) {
        if (depth > 0 && Array.isArray(value)) {
            if (depth > 1) {
                flatten(value, depth - 1, isStrict, result)
            } else {
                result.push(...value); // 只拆1層
            }
        } else if (!isStrict) {
            result[result.length] = value        }
    }
    return result;}
  1. 該方法提供了扁平化層數
const res = flatten([1, 2, [3, 4, [5, 6]], { a: 1 }, null, undefined], 1, false);// [ 1, 2, 3, 4, [ 5, 6 ], { a: 1 }, null, undefined ]
  1. 該方法提供了扁平化後相反的效果
const res = flatten([1, 2, [3, 4, [5, 6]], { a: 1 }, null, undefined], 1, true);// [ 3, 4, [ 5, 6 ] ]const res = flatten([1, 2, [3, 4, [5, 6]], { a: 1 }, null, undefined], 2, true);// [ 5, 6 ]

isStrict引數開啟後,扁平後保留了被暴漏出來的元素,剔除了淺層元素。

寫到這裡我們即瞭解了扁平化的處理思路,也有了一定的實現能力,如果你能完全理解上面的程式碼,相信扁平化這一部分應該難不到你了,我們下一篇文章繼續研究loadsh的另一個方法~

相關免費學習推薦:(視訊)

以上就是JavaScript專題之八:陣列扁平化的詳細內容,更多請關注TW511.COM其它相關文章!