陪你去看 Lodash.js 起步

2022-11-14 12:11:37

lodash 起步(陣列)

Lodash 是一個較為流行的 JavaScript 的實用工具庫。

在開發過程中如果能熟練使用一些工具庫提供的方法,有利於提高開發效率

筆者從 API 上入手,不分析其原始碼,先從全域性的角度來體驗、研究它。分析 API 的命名特性、哪些方法有用,哪些又是比較雞肋,僅代表筆者個人意見。

Tip:根據Lodash 中文檔案得知,API 主要分為:陣列、集合、函數、語言、數學、數位、物件、Seq、字串、實用函數等約10個部分。而陣列在我們程式設計過程中具有非常重要的地位,故筆者決定先從其入手。

環境準備

直接通過參照 bootstrap 免費的 cdn 的方式進行,只需兩個檔案:index.htmltest-array.js。內容如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.js"></script>
    <script src="./test-array.js"></script>
</head>
<body>
    
</body>
</html>
let r

// 封裝 console.log,使之更剪短
const l = (...ops) => {
    console.log(...ops)
}

{
    r = _.VERSION
    // => 4.17.21
    l(r)
}

{
    r = _.join(['a', 'b', 'c'], '~');
    // => 'a~b~c'
    l(r)
}

Tip: 所有測試都是如上結構

api 設計特性分析

  1. 有的方法可能是考慮相容性,比如 _.fill,但 javascript 也有對應的原生方法(Array.prototype.fill),倘若使用 babel 庫幫忙轉換,那這類方法就有些雞肋
  2. 命名系列化。比如下文的 _.sortedIndex 系列,有 .sortedIndex、.sortedIndexOf、.sortedLastIndexOf、.sortedIndexBy、.sortedLastIndex、.sortedLastIndexBy 共6個,核心是 _.sortedIndex,通常系列方法有功能增強版,例如這裡的 _.sortedIndexBy,而命名中有 Last 通常從後往前查詢
  3. 系列化的 api 通常有基礎版和加強版。例如 _.difference 系列,_.differenceBy_.differenceWith都是加強版
  4. 提供簡寫進一步提高程式碼簡潔性,比如下面的 _.isEqual
const objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
_.differenceWith(objects, [{ 'x': 1, 'y': 2 }], _.isEqual);
// => [{ 'x': 2, 'y': 1 }]

陣列 api 總覽

僅列出非雞肋方法:
Tip: 筆者認為可以用原生方法非常便捷的替換的 api 就是雞肋。

  • _.chunk - 分塊。將陣列(array)拆分成多個 size 長度的區塊,並將這些區塊組成一個新陣列
  • _.compact - 壓緊。建立一個新陣列,包含原陣列中所有的非假值元素
  • _.difference 系列 - 。從第一個陣列中排除給定陣列中的值
  • _.drop 系列 - 減小。例如去除陣列前面兩個元素
  • _.fromPairs/_.toPair - 一雙。例如 [['fred', 30], ['barney', 40]] <---> { 'fred': 30, 'barney': 40 }
  • _.intersection 系列 - 交集
  • _.pull - 移除陣列中的值
  • _.sortedIndex - value 應插入已排序陣列中的索引
  • _.sortedUniq - 唯一。對已排序的陣列過濾重複值
  • _.take - 切片(從左提取 n 個元素)
  • _.union - 並集
  • _.uniq - 唯一(用於去重)
  • _.zip - 壓縮[1, 2], [10, 20], [100, 200] -> [[1, 10, 100], [2, 20, 200]]
  • _.unzip 系列 - 類似 _.zip。[[1, 2], [10, 20], [100, 200]] -> [[1, 10, 100], [2, 20, 200]]
  • _.without - 缺乏。可用 array.filter 替代。
  • _.xor 系列 - X集。只存在於某個陣列中的值。

陣列 api 詳細

_.chunk

_.chunk - 分塊。

// _.chunk(array, [size=1]) - 將陣列(array)拆分成多個 size 長度的區塊,並將這些區塊組成一個新陣列。 如果array 無法被分割成全部等長的區塊,那麼最後剩餘的元素將組成一個區塊。
{
    r = _.chunk(['a', 'b', 'c', 'd'], 2)
    // => [['a', 'b'], ['c', 'd']]
    l(r)

    r = _.chunk(['a', 'b', 'c', 'd'], 3)
    // => [['a', 'b', 'c'], ['d']]
    l(r)
}

_.compact

_.compact - 壓緊。可用 array.filter 實現

// _.compact(array) - 建立一個新陣列,包含原陣列中所有的非假值元素。例如false, null,0, "", undefined, 和 NaN 都是被認為是「假值」。
{
    // 返回過濾掉假值的新陣列
    // 例如 false, null, 0, "", undefined, NaN 都是被認為是「假值」
    r = _.compact([false, null, 0, "", undefined, NaN, 1]);
    // => [1]
    l(r)

    // 可用 array.filter 實現
    r = [false, null, 0, "", undefined, NaN, 1].filter(v => !!(v))
    // => [1]
    l(r)
}

_.difference 系列

_.difference系列 - 差(從第一個陣列中排除給定陣列中的值)。_.differenceBy_.differenceWith都是加強版。

// _.difference(array, [values]) - 建立一個具有唯一array值的陣列,每個值不包含在其他給定的陣列中
// _.differenceBy(array, [values], [iteratee=_.identity]) - 這個方法類似_.difference ,除了它接受一個 iteratee (注:迭代器), 呼叫array 和 values 中的每個元素以產生比較的標準。
// _.differenceWith(array, [values], [comparator]) - 這個方法類似_.difference ,除了它接受一個 comparator (注:比較器),它呼叫比較array,values中的元素。 結果值是從第一陣列中選擇
{

    // 從第一個陣列中排除給定陣列中的值。
    // 注:非陣列會忽略
    r = _.difference([1, 2, 3, 4, 5, 6], [1, 2], [3], [4], 5, [6])
    // 5
    l(r)

    // 與 difference 類似。不同之處是每個值得呼叫 Math.floor 在比較,比如 2.2 和 2.5 認為是相等的
    r = _.differenceBy([3.1, 2.2, 1.3], [4.4, 2.5], Math.floor);
    // => [3.1, 1.3]
    l(r)

    // The `_.property` iteratee shorthand.
    r = _.differenceBy([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], 'x');
    // => [{ 'x': 2 }]
    l(r)

    var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
    // 與 difference 類似。不同之處是每個值得呼叫 _.isEqual 這個比較器
    // 與 differenceBy 區別,感覺前者是迭代每個值並從中取得值進行比較,而 differenceWith 直接用比較器直接比較
    r = _.differenceWith(objects, [{ 'x': 1, 'y': 2 }], _.isEqual);
    // => [{ 'x': 2, 'y': 1 }]
    l(r)
}

_.drop 系列

_.drop系列 - 減小(例如去除陣列前面兩個元素)。某些場景可以使用 ... 結構語法替換;_.dropRightWhile_.dropWhile 都是增強版。

// _.drop(array, [n=1]) - 建立一個切片陣列,去除array前面的n個元素。(n預設值為1。)
// _.dropRight(array, [n=1]) - 建立一個切片陣列,去除array尾部的n個元素。(n預設值為1。)
// _.dropRightWhile(array, [predicate=_.identity]) - 建立一個切片陣列,去除array中從 predicate 返回假值開始到尾部的部分
// _.dropWhile(array, [predicate=_.identity]) - 建立一個切片陣列,去除array中從起點開始到 predicate 返回假值結束部分
{
    // 去除前面兩個元素
    r = _.drop([1, 2, 3], 2);
    // => [3]
    l(r);

    // 用...解構語法替代上面例子
    [, , ...r] = [4, 5, 6]
    // => [6]
    l(r)

    // 去除後面兩個元素
    r = _.dropRight([1, 2, 3], 2);
    // => [1]
    l(r)

    // 作用難以理解。直接用 filter
    var users = [
        { 'user': 'barney', 'active': true },
        { 'user': 'fred', 'active': false },
        { 'user': 'pebbles', 'active': false }
    ];

    r = _.dropRightWhile(users, function (o) { return !o.active; });
    // => [{ 'user': 'barney', 'active': true }]
    l(r)
}

_.fromPairs & _.toPair

_.fromPairs & _.toPair - 一雙。前者可用 array.reduce 實現,後者可用 Object.entries 方便的替代。

// _.fromPairs(pairs) - 與_.toPairs正好相反;這個方法返回一個由鍵值對pairs構成的物件。
{
    let arr = [['fred', 30], ['barney', 40]]
    r = _.fromPairs(arr);
    // => { 'fred': 30, 'barney': 40 }
    l(r)

    // 用 reduce 重寫上述例子
    r = arr.reduce((obj, [key, value]) => {
        obj[key] = value
        return obj
    }, {})
    // => {fred: 30, barney: 40}
    l(r)

    r = _.toPairs(r)
    // => [['fred', 30], ['barney', 40]]
    l(r)

    // 用 Object.entries() 重寫上一個範例
    r = { fred: 30, barney: 40 }
    // Object.entries()方法返回一個給定物件自身可列舉屬性的鍵值對陣列
    r = Object.entries(r)
    // => [['fred', 30], ['barney', 40]]
    l(r)
}

_.intersection 系列

_.intersection - 交集。_.intersectionBy_.intersectionWith 是增強版。

// _.intersection([arrays]) - 陣列的交集
// _.intersectionBy([arrays], [iteratee=_.identity]) - 這個方法類似_.intersection,區別是它接受一個 iteratee 呼叫每一個arrays的每個值以產生一個值,通過產生的值進行了比較
// _.intersectionWith([arrays], [comparator]) - 這個方法類似_.intersection,區別是它接受一個 comparator 呼叫比較arrays中的元素。結果值是從第一陣列中選擇
{
    r = _.intersection([1, 2], [2, 3], [2, 3, 4]);
    // => [2]
    l(r)

    r = _.intersection([1, 2], [3, 4]);
    // => []
    l(r)

    r = _.intersectionBy([2.1, 1.2], [4.3, 2.4], Math.floor);
    // => [2.1]
    l(r)

    const objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
    const others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];

    r = _.intersectionWith(objects, others, _.isEqual);
    // => [{ 'x': 1, 'y': 2 }]
    l(r)
}

_.pull 系列

_.pull - 移除陣列中的值。_.pullAt 根據索引移除,_.pullAll 系列都是加強版。某些場景可使用 array.filter 替代。

// _.pull(array, [values]) - 移除陣列array中所有和給定值相等的元素,使用SameValueZero 進行全等比較
// _.pullAll(array, values) - 這個方法類似_.pull,區別是這個方法接收一個要移除值的陣列。
// _.pullAllBy(array, values, [iteratee=_.identity]) - 這個方法類似於_.pullAll ,區別是這個方法接受一個 iteratee(迭代函數) 呼叫 array 和 values的每個值以產生一個值,通過產生的值進行了比較。
// _.pullAllWith(array, values, [comparator]) - 這個方法類似於_.pullAll,區別是這個方法接受 comparator 呼叫array中的元素和values比較。
// _.pullAt(array, [indexes]) - 根據索引 indexes,移除array中對應的元素,並返回被移除元素的陣列。Tip:刪除非連續的多個則不方便用 array.splice 替代
{
    r = [1, 2, 3, 1, 2, 3]
    _.pull(r, 2, 3);
    l(r)
    // => [1, 1]

    // 用 array.filter  重寫上一個範例
    r = [1, 2, 3, 1, 2, 3];
    r = r.filter(item => item !== 2 && item !== 3)
    // => [1, 1]
    l(r)

    // 陣列中移除 { 'x': 3, 'y': 4 }
    r = [{ 'x': 1, 'y': 2 }, { 'x': 3, 'y': 4 }, { 'x': 5, 'y': 6 }];
    _.pullAllWith(r, [{ 'x': 3, 'y': 4 }], _.isEqual);
    // => [{ 'x': 1, 'y': 2 }, { 'x': 5, 'y': 6 }]
    l(r)

    // 移除索引 1 和 3。
    r = [5, 10, 15, 20];
    _.pullAt(r, 1, 3);
    // => [5, 15]
    l(r)
}

_.sortedIndex 系列

_.sortedIndex - value 應插入已排序陣列中的索引。 _.sortedIndexBy 是增強版。_.sortedIndexOf[雞肋] 和 array.indexOf 類似。

:陣列要已排序,否則得不到預期值。

// _.sortedIndex(array, value) - 使用二進位制的方式檢索來決定 value值 應該插入到陣列中 儘可能小的索引位置,以保證array的排序。
// _.sortedIndexOf(array, value) - 這個方法類似_.indexOf,除了它是在已經排序的陣列array上執行二進位制檢索。
// _.sortedLastIndexOf(array, value) - 這個方法類似_.lastIndexOf
// _.sortedIndexBy(array, value, [iteratee=_.identity]) - 這個方法類似_.sortedIndex
// _.sortedLastIndex(array, value) - 此方法類似於_.sortedIndex
// _.sortedLastIndexBy(array, value, [iteratee=_.identity]) - 這個方法類似_.sortedLastIndex
{
    // 陣列有序,40 所放索引正確
    r = _.sortedIndex([20, 30, 50], 40)
    // => 2
    l(r)

    // 陣列無序,筆者自己都不知道 40 應放哪裡
    r = _.sortedIndex([50, 20, 30], 40)
    // => 3
    l(r)

    r = [{ 'x': 4 }, { 'x': 5 }];
    r = _.sortedIndexBy(r, { 'x': 4.5 }, function (o) { return o.x; });
    // => 1
    l(r)

    // 和 array.indexOf 類似
    r = _.sortedIndexOf([4, 5, 5, 5, 6], 5);
    // => 1
    l(r)

    r = [4, 5, 5, 5, 6].indexOf(5)
    // => 1
    l(r)

    // _.sortedLastIndex(array, value) - 和 array.lastIndexOf() 類似
}

_.sortedUniq 系列

_.sortedUniq - 唯一。對已排序的陣列過濾重複值,可用 Set 過濾。_.sortedUniqBy 是增強版

// _.sortedUniq(array) - 這個方法類似_.uniq,除了它會優化排序陣列。
// _.sortedUniqBy(array, [iteratee]) - 這個方法類似_.uniqBy,除了它會優化排序陣列。
{
    r = _.sortedUniq([1, 1, 2]);
    // => [1, 2]
    l(r)

    // 用 Set 過濾重複值
    r = [...new Set([1, 1, 2])]
    // => [1, 2]
    l(r)


    r = _.sortedUniqBy([1.1, 1.2, 2.3, 2.4], Math.floor);
    // => [1.1, 2.3]
    l(r)

    // 利用一個臨時陣列實現上一個範例
    r = [1.1, 1.2, 2.3, 2.4]
    const tmp = []
    r = r.filter(item => {
        let v = Math.floor(item)
        if (tmp.includes(v)) {
            return false
        }
        tmp.push(v)
        return true
    })
    // => [1.1, 2.3]
    l(r)
}

_.take 系列

_.take - 切片(從左提取 n 個元素)。_.take 和 _.takeRight 可用 array.slice 替代。 _.takeWhile 是增強版。

// _.take(array, [n=1]) - 建立一個陣列切片,從array陣列的起始元素開始提取n個元素。
// _.takeRight(array, [n=1]) - 建立一個陣列切片,從array陣列的最後一個元素開始提取n個元素。
// _.takeWhile(array, [predicate=_.identity]) - 與 _.takeRightWhile 類似,只是方向不同
// _.takeRightWhile(array, [predicate=_.identity]) - 從array陣列的最後一個元素開始提取元素,直到 predicate 返回假值
{
    r = _.take([1, 2, 3], 2);
    // => [1, 2]
    l(r)

    // 可用 array.slice 重寫上一個範例
    r = [1, 2, 3].slice(0, 2)
    // => [1, 2]
    l(r)

    r = _.takeRight([1, 2, 3, 4], 2);
    // => [3, 4]
    l(r)

    // 可用 array.slice 重寫上一個範例
    r = [1, 2, 3, 4].slice(-2)
    // => [3, 4]
    l(r)

    const users = [
        { 'user': 'barney', 'active': true },
        { 'user': 'fred', 'active': false },
        { 'user': 'pebbles', 'active': false }
    ];
    r = _.takeRightWhile(users, function (o) { return !o.active; });
    // => objects for ['fred', 'pebbles']
    l(r)

    // 自己實現 _.takeRightWhile
    r = []
    users.reverse().forEach(item => {
        if (item.active) {
            return false
        }
        r.push(item)
    })
    r.reverse()
    // => objects for ['fred', 'pebbles']
    l(r)
}

_.union 系列

_.union - 並集。_.union 可用 Set 方便實現。_.unionBy_.unionWith 是增強版。

// _.union([arrays]) - 建立一個按順序排列的唯一值的陣列。所有給定陣列的元素值使用SameValueZero做等值比較。
// _.unionBy([arrays], [iteratee=_.identity]) - 這個方法類似_.union ,除了它接受一個 iteratee (迭代函數),呼叫每一個陣列(array)的每個元素以產生唯一性計算的標準
// _.unionWith([arrays], [comparator]) - 這個方法類似_.union, 除了它接受一個 comparator 呼叫比較arrays陣列的每一個元素。例如比較物件
{
    r = _.union([1, 3, 5, 7, 3], [6, 4, 3]);
    // => [1, 3, 5, 7, 6, 4]
    l(r)

    // 兩個陣列也可以用 Set 實現
    r = [...new Set([...[1, 3, 5, 7, 3], ...[6, 4, 3]])]
    // or r = Array.from(new Set([...[1, 3, 5, 7], ...[6, 4, 3]]))
    // => [1, 3, 5, 7, 6, 4]
    l(r)

    // 比較處理後的值
    r = _.unionBy([2.1], [1.2, 2.3], Math.floor)
    // => [2.1, 1.2]
    l(r)

    // 比較物件。物件的唯一性
    const objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
    const others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];

    r = _.unionWith(objects, others, _.isEqual);
    // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]
    l(r)
}

_.uniq 系列

_.uniq - 唯一(用於去重)。_.uniq 可用 Set 方便實現。_.uniqBy _.uniqWith 是增強版。

// _.uniq(array) - 建立一個去重後的array陣列副本。
// _.uniqBy(array, [iteratee=_.identity]) - 這個方法類似_.uniq ,除了它接受一個 iteratee (迭代函數),呼叫每一個陣列(array)的每個元素以產生唯一性計算的標準
// _.uniqWith(array, [comparator]) - 這個方法類似_.uniq, 除了它接受一個 comparator 呼叫比較arrays陣列的每一個元素。例如比較物件
{
    r = _.uniq([2, 1, 2]);
    // => [2, 1]
    l(r)

    r = [...new Set([2, 1, 2])]
    // => [2, 1]
    l(r)

    // 比較處理後的值
    r = _.uniqBy([2.1, 1.2, 2.3], Math.floor);
    // => [2.1, 1.2]
    l(r)

    //  比較物件
    r = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }];

    r = _.uniqWith(r, _.isEqual);
    // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]
    l(r)
}

_.zip 系列

_.zip - 壓縮。_.zipWith_.zipObject_.zipObjectDeep都是增強版

// _.zip([arrays]) - 建立一個分組元素的陣列,陣列的第一個元素包含所有給定陣列的第一個元素,陣列的第二個元素包含所有給定陣列的第二個元素,以此類推。
// _.zipWith([arrays], [iteratee=_.identity]) - 這個方法類似於_.zip,不同之處在於它接受一個 iteratee(迭代函數),來指定分組的值應該如何被組合。
// _.zipObject([props=[]], [values=[]]) - 這個方法類似_.fromPairs,除了它接受2個陣列,第一個陣列中的值作為屬性識別符號(屬性名),第二個陣列中的值作為相應的屬性值。
// _.zipObjectDeep([props=[]], [values=[]])- 這個方法類似_.zipObject,除了它支援屬性路徑。
{
    r = _.zip([1, 2], [10, 20], [100, 200])
    // => [[1, 10, 100], [2, 20, 200]]
    l(r)

    // 自己實現一個弱版本
    const zip = (...arrs) => {
        const result = []
        arrs[0].forEach((v, index) => {
            const tmp = []
            arrs.forEach(item => tmp.push(item[index]))
            result.push(tmp)
        })
        return result
    }

    r = zip([1, 2], [10, 20], [100, 200])
    // => [[1, 10, 100], [2, 20, 200]]
    l(r)

    r = _.zipWith([1, 2], [10, 20], [100, 200], function (a, b, c) {
        return a + b + c;
    });
    // => [111, 222]
    l(r)

    // 組合成物件
    r = _.zipObject(['a', 'b'], [1, 2]);
    // => { 'a': 1, 'b': 2 }
    l(r)

    r = _.zipObjectDeep(['a.b[0].c', 'a.b[1].d'], [1, 2])
    // => { 'a': { 'b': [{ 'c': 1 }, { 'd': 2 }] } }
    l(r)
}

_.unzip 系列

_.unzip - 解壓。似於_.zip。_.unzipWith 是增強版。

// _.unzip(array) - 這個方法類似於_.zip,除了它接收分組元素的陣列,並且建立一個陣列,分組元素到打包前的結構。(:返回陣列的第一個元素包含所有的輸入陣列的第一元素,第一個元素包含了所有的輸入陣列的第二元素,依此類推。)
// _.unzipWith(array, [iteratee=_.identity]) - 此方法類似於_.unzip,除了它接受一個iteratee指定重組值應該如何被組合。增強版
{
    r = _.unzip([1, 2], [10, 20], [100, 200])
    // => []
    l(r)
    // 用陣列包裹後,就和 _.zip 效果相同
    r = _.unzip([[1, 2], [10, 20], [100, 200]])
    // => [[1, 10, 100], [2, 20, 200]]
    l(r)

    r = _.unzipWith([[1, 10, 100], [2, 20, 200]], _.add);
    // => [3, 30, 300]
    l(r)
}

_.without

_.without - 缺乏。可用 array.filter 替代

// _.without(array, [values]) - 建立一個剔除所有給定值的新陣列。
{
    r = _.without([2, 1, 2, 3], 1, 2);
    // => [3]
    l(r)

    r = [2, 1, 2, 3].filter(item => ![1, 2].includes(item))
    // => [3]
    l(r)
}

_.xor 系列

_.xor - X集(只存在於某個陣列中的值)。_.xorBy_.xorWith 是增強版

// _.xor([arrays]) - 建立一個給定陣列唯一值的陣列,使用symmetric difference做等值比較。返回值的順序取決於他們陣列的出現順序。每個元素的差集,即只存在於某個陣列中的值,其他陣列沒有改值。
// _.xorBy([arrays], [iteratee=_.identity]) - 這個方法類似_.xor ,除了它接受 iteratee(迭代器),這個迭代器 呼叫每一個 arrays(陣列)的每一個值,以生成比較的新值。增強版
// _.xorWith([arrays], [comparator]) - 該方法是像_.xor,除了它接受一個 comparator ,以呼叫比較陣列的元素。增強版
{
    r = _.xor([1, 2], [3, 2], [4, 2], [5])
    // => [1, 3, 4, 5]
    l(r)
    r = _.xor([1, 2], [3, 2], [4, 2, 5], [5])
    // => [1, 3, 4]
    l(r)

    // 轉換後的值比較
    r = _.xorBy([2.1, 1.2], [2.3, 3.4], Math.floor);
    // => [1.2, 3.4]
    l(r)

    // 物件比較
    const objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
    const others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
    r = _.xorWith(objects, others, _.isEqual);
    // => [{ 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]
    l(r)
}

雞肋

_.concat

_.concat - 連線陣列。與 array.concat 效果相同,也可用 es6+ 語法 ... 實現

// _.concat(array, [values]) - 建立一個新陣列,將array與任何陣列 或 值連線在一起。
// 
{
    const array = [1];
    r = _.concat(array, 2, [3], [[4]]);

    console.log(r);
    // => [1, 2, 3, [4]]

    // 與原生陣列的 concat 效果相同
    r = array.concat(2, [3], [[4]])
    // => [1, 2, 3, [4]]
    l(r)

    r = [...array, 2, ...[3], ...[[4]]]
    // [1, 2, 3, [4]]
    l(r)
}
_.fill

_.fill - 填充。可用 array.fill 替代

// _.fill(array, value, [start=0], [end=array.length]) - 使用 value 值來填充(替換) array,從start位置開始, 到end位置結束(但不包含end位置)。
{

    let array = [1, 2, 3];
    r = [...array]
    _.fill(r, 'a');
    l(r)
    // => ['a', 'a', 'a']

    // array.fill 重寫上一個範例
    // fill() 方法用一個固定值填充一個陣列中從起始索引到終止索引內的全部元素。不包括終止索引。
    r = [...array]
    r.fill('b')
    l(r)
    // =>  ['b', 'b', 'b']

    r = [4, 6, 8, 10]
    _.fill(r, '*', 1, 3)
    l(r)
    // => [4, '*', '*', 10]

    // array.fill 重寫上一個範例
    r = [4, 6, 8, 10]
    r.fill('*', 1, 3)
    l(r)
    // => [4, '*', '*', 10]
}

_.findIndex

_.findIndex - 可用 Array.prototype.findIndex 替代

// _.findIndex(array, [predicate=_.identity], [fromIndex=0]) - 該方法類似_.find,區別是該方法返回第一個通過 predicate 判斷為真值的元素的索引值(index),而不是元素本身。
// _.findLastIndex(array, [predicate=_.identity], [fromIndex=array.length-1])
{
    let users = [
        { 'user': 'fred', 'active': false },
        { 'user': 'fred', 'active': true },
        { 'user': 'pebbles', 'active': true }
    ];

    r = _.findIndex(users, function (o) { return o.user == 'fred' && o.active === false });
    // => 0
    l(r)

    // array.findIndex 重寫上一個範例
    // findIndex()方法返回陣列中滿足提供的測試函數的第一個元素的索引。若沒有找到對應元素則返回 -1。
    r = users.findIndex(o => o.user == 'fred' && o.active === false)
    // => 0
    l(r)

    // The `_.matches` iteratee shorthand.
    r = _.findIndex(users, { 'user': 'fred', 'active': true });
    // => 1
    l(r)
}
_.head

_.head - 可用 arr[index] 或 att.at(index) 替代

// _.head(array) - 獲取陣列 array 的第一個元素。可用 arr[index] 或 att.at(index) 替代。
{
    const arr = [2, 3, 4]
    r = _.head(arr)
    // => 2
    l(r)

    // 需要 chrome 92+
    // r = arr.at(0)
    // => 2
    // l(r)
}
_.flatten

_.flatten - 變平。array.flat 能非常方便的替代這3個api。

// _.flatten(array) - 減少一級array巢狀深度。
// _.flattenDeep(array) - 將array遞迴為一維陣列。
// _.flattenDepth(array, [depth=1]) - 根據 depth 遞迴減少 array 的巢狀層級
{
    let arr = [1, [2, [3, [4]], 5]]
    r = _.flatten(arr);
    // => [1, 2, [3, [4]], 5]
    l(r)

    // array.flat 重寫上一個範例
    r = arr.flat()
    // => [1, 2, [3, [4]], 5]
    l(r)

    
    r = _.flattenDeep(arr)
    // => [1, 2, 3, 4, 5]
    l(r)

    // 用 array.flat 重寫上一個範例
    r = arr.flat(Infinity)
    // => [1, 2, 3, 4, 5]
    l(r)
}
_.remove

_.remove - 刪除。可用 array.filter 替代。

// _.remove(array, [predicate=_.identity]) - 移除陣列中predicate(斷言)返回為真值的所有元素,並返回移除元素組成的陣列
{
    let array = [1, 2, 3, 4];
    r = _.remove(array, function (n) {
        return n % 2 == 0;
    });

    // => [2, 4]
    l(r)

    // 用 array.filter 替代
    r = [1, 2, 3, 4]
    r = r.filter(item => item % 2 === 0)
    // => [2, 4]
    l(r)
}
_.tail

_.tail - 尾部。可用 array.slice 替代

// _.tail(array) - 獲取除了array陣列第一個元素以外的全部元素。
{
    r = _.tail([1, 2, 3]);
    // => [2, 3]
    l(r)

    r = [1, 2, 3].slice(1)
    // => [2, 3]
    l(r)
}
其他
  • _.initial(array) - 獲取陣列array中除了最後一個元素之外的所有元素。名字不好記。可用 [1, 2, 3].slice(0, -1) 替代 或 let [, ...r] = [1,2,3].reverse(); r.reverse()

  • _.join(array, [separator=',']) - 可用 Array.prototype.join 替代

  • _.last(array) - 獲取array中的最後一個元素。 可用 array[array.length - 1] 替代

  • _.lastIndexOf(array, value, [fromIndex=array.length-1]) - 可用 Array.prototype.lastIndexOf 替代

  • _.nth(array, [n=0]) - 獲取array陣列的第n個元素。直接用陣列索引獲取 或 Array.prototype.at 替代

  • _.reverse(array) - 逆序。可用 array.reverse 替代。

  • _.slice(array, [start=0], [end=array.length]) - slice。可用 array.slice 替代

  • _.indexOf - 可用 Array.prototype.indexOf 替代