JS 扁平化(flatten) 數(shù)組

前言

數(shù)組是 JS 中使用頻率僅次于對象的數(shù)據(jù)結(jié)構(gòu),官方提供了眾多的 API,今天我們來談?wù)勅绾伪馄交╢latten)數(shù)組。

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

通常有幾種方法可以實現(xiàn)扁平化:

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

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

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

console.log(result)

迭代遞歸

for...of 實現(xiàn)

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

我們使用 result 變量存儲結(jié)果,然后迭代當(dāng)前數(shù)組,如果值也是數(shù)組則繼續(xù)扁平化,否則將值放入 result 里。

迭代器實現(xiàn)

眾所周知,數(shù)組在 JS 中是一種可迭代結(jié)構(gòu),所以我們可以利用這一點修改它的迭代器實現(xiàn)扁平化:

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,
                }
            }
        },
    }
}

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

生成器實現(xiàn)

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

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
}

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

這里有兩點需要注意:

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

reduce 三句實現(xiàn)法

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

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

曲線救國法

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

降維打擊法

function flatten(arr){
    let str = arr.toString()
    return str.split(',')
}

管你原來是幾維,先來個二向箔:轉(zhuǎn)成字符串,之后再復(fù)原成數(shù)組,不過這個方法有個缺點,就是原來的空數(shù)組轉(zhuǎn)的空字符串也會被放入新生成的數(shù)組里去。所以如果不需要空串元素的話還需要對結(jié)果進行過濾操作。
除了直接調(diào)用它的 toString 方法之外,還可以用隱式轉(zhuǎn)換間接調(diào)用:

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

lodash 層次法

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

  • 淺扁平化就是只扁平化一層數(shù)組
  • 深扁平化就是迭代調(diào)用淺扁平化函數(shù)

而淺扁平化有下列實現(xiàn)方法:

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

或者

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

所以我們最終實現(xiàn)的是:

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

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

最后要說的

flatten 轉(zhuǎn)眼間就要成為 ES 的標(biāo)準(zhǔn)數(shù)組 API 了,但發(fā)生了很多有意思的事情

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 你不知道JS:異步 第四章:生成器(Generators) 在第二章,我們明確了采用回調(diào)表示異步流的兩個關(guān)鍵缺點:...
    purple_force閱讀 1,043評論 0 2
  • 戳下邊藍(lán)字 無提取碼 百度云 日在校園全集 更多動漫 戳右邊藍(lán)字→Neets.cc-日在校園
    總想取個不同的昵稱閱讀 8,028評論 0 0
  • 就算是討厭的人對你說話,最起碼的尊重要給他,聽的時候要可以對話不對人,有理我們聽無理就無視好了,我們討厭的人身...
    煙花雨下的諾言閱讀 210評論 0 0
  • canvas 畫板 當(dāng)在畫布中,如果按下鼠標(biāo),我們將畫布的起始點放在此時鼠標(biāo)的位置,用到的是moveTo(),然后...
    放飛吧自我閱讀 498評論 0 1
  • 在成堆的書下找到一個屬于自己的空間,靜謐又安逸。
    就苦瓜閱讀 334評論 2 2

友情鏈接更多精彩內(nèi)容