JS 扁平化(flatten) 數組


前言

數組是 JS 中使用頻率僅次於對象的數據結構,官方提供了眾多的 API,今天我們來談談如何扁平化(flatten)數組。

顧名思義,扁平化就是將嵌套的數組變成一維數組的過程。

通常有幾種方法可以實現扁平化:

  1. 迭代遞歸法
  2. 曲線救國法

我們將以一個例子貫穿整篇文章:

var array = [[1,2,3],4,5,6,[[7]],[]]
var result = flatten(array)

console.log(result)

一、迭代遞歸

for...of 實現

function flatten(arr, result = []) {
    for (let item of arr) {
        if (Array.isArray(item))
            flatten(item, result)
        else
            result.push(item)
    }
    return result
}

我們使用 result 變量存儲結果,然后迭代當前數組,如果值也是數組則繼續扁平化,否則將值放入 result 里。

迭代器實現

眾所周知,數組在 JS 中是一種可迭代結構,所以我們可以利用這一點修改它的迭代器實現扁平化:

Array.prototype[Symbol.iterator] = function() {
    let arr = [].concat(this)
    const getFirst = function(array) {
        let first = array[0]
        // 去掉為 [] 的元素
        while (Array.isArray(array[0]) && array.length === 0) {
           array.shift()
        }
        if (Array.isArray(first)) {
            // 即將是 []
            if (first.length === 1) array.shift()
            return getFirst(first)
        } else {
            array.shift()
            return first
        }
    }
    return {
        next: function() {
            let item = getFirst(arr)
            if (item) {
                return {
                    value: item,
                    done: false,
                }
            } else {
                return {
                    done: true,
                }
            }
        },
    }
}

這里我們給數組的迭代器函數重新定義了 next 方法,實現了一個 getFirst 用來遞歸取真正的第一個數組元素(無論嵌套多少層),在對數組進行迭代操作的時候,會自動調用迭代器的 next 方法,獲得我們一個個基本元素。
不過這樣太麻煩了,還不如第一種方法方便呢!別急,下面那個實現才是我們想給大家看的~

生成器實現

迭代器的升級版就是生成器(Generator),其實這種扁平化最適合用生成器來做了,因為我們的目的就是生成一個個的值,然后把它們組織成一維數組:

function* flat(arr) {
    for (let item of arr) {
        if (Array.isArray(item))
            yield* flat(item)
        else
            yield item
    }
}

function flatten(arr) {
    let result = []
    for (let val of flat(arr)) {
        result.push(val)
    }
    return result
}

是不是很簡潔明了?只需要定義一個生成器函數:迭代當前數組,如果值也是數組則生成扁平化的值,否則直接生成值。然后在我們的扁平化函數里調用這個生成器函數得到我們的一維數組。

這里有兩點需要注意:

  1. 嵌套 yield 需要再加一個星號,這被稱為生成器委托。
  2. 不能使用 forEach 代替 for...of 但可以用 for 循環,因為 for 循環和for...of 可以中斷迭代去執行 yield,forEach 不行,有興趣的讀者可以自己嘗試一下~

reduce 三句實現法

function flatten(arr) {
  return arr.reduce((flat, toFlatten) => {
    return flat.concat(Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten);
  }, []);
}

reduce 是函數式編程兩大法寶之一,中文翻譯為化簡,用它來實現,簡直是巧妙。

二、曲線救國法

這些方法大多是利用 JS 本身的一些特性和 API,算是奇技淫巧。

降維打擊法

function flatten(arr){
    let str = arr.toString()
    return str.split(',')
}
管你原來是幾維,先來個二向箔:轉成字符串,之后再復原成數組,不過這個方法有個缺點,就是原來的空數組轉的空字符串也會被放入新生成的數組里去。所以如果不需要空串元素的話還需要對結果進行過濾操作。
除了直接調用它的 toString 方法之外,還可以用隱式轉換間接調用:
function flatten(arr){
    return (arr + '').split(',')
}

lodash 層次法

lodash 分為淺扁平化和深扁平化(deepFlatten)兩個方法。

  • 淺扁平化就是只扁平化一層數組
  • 深扁平化就是迭代調用淺扁平化函數

而淺扁平化有下列實現方法:

function shallowFlatten(arr){
    return [].concat.apply([],arr)
}

或者

function shallowFlatten(arr) {
  return arr.reduce((a, b) => a.concat(b), [])
}

所以我們最終實現的是:

function flatten(arr,n=1){
  let result = arr
  while(n--){
    result = shallowFlatten(result)
  }
  return result 
}

唯一變化的是調用方法從flatten(array)變成了flatten(array,2)。

三、ES 的標准數組 API:arr.flat(Infinity)數組扁平化

 

轉自:https://www.jianshu.com/p/b1fb3434e1f5

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM