lodash已死?radash最全使用介紹(附原始碼說明)—— Array方法篇(3)

雾散声声慢發表於2024-04-12

前言

  • 我們已經介紹了radash的相關資訊和部分Array相關方法,詳情可前往主頁檢視;
  • 本篇我們繼續介紹radash中Array的相關方法;
  • 下期我們將介紹解析radash中剩餘的 Array相關方法,並整理出Array方法使用目錄,包括文章說明腦圖說明

Radash的Array相關方法詳解

iterate:把一個函式迭代執行指定次數

  1. 使用說明
    • 引數:迭代次數、每次迭代呼叫的函式、迭代初始值。
    • 返回值:返回最終一次迴圈迭代的值。
  2. 使用程式碼示例
    import { iterate } from 'radash'
    
    const value = iterate(
      4,
      (acc, idx) => {
        return acc + idx
      },
      0
    ) // => 10
    
  3. 原始碼解析
    // 定義一個泛型函式 `iterate`,它接收三個引數:
    // `count` 是一個數字,表示迭代的次數;
    // `func` 是一個函式,它在每次迭代中被呼叫,接收當前值和迭代次數作為引數,並返回一個新的值;
    // `initValue` 是迭代的初始值,它的型別為泛型 `T`。
    export const iterate = <T>(
      count: number,
      func: (currentValue: T, iteration: number) => T,
      initValue: T
    ) => {
      // 初始化一個變數 `value`,用於儲存當前的迭代值,起始值設定為 `initValue`。
      let value = initValue
      // 使用一個 `for` 迴圈進行 `count` 次迭代。迴圈變數 `i` 從 1 開始,以確保迭代次數正確。
      for (let i = 1; i <= count; i++) {
        // 在每次迭代中,呼叫函式 `func`,傳入當前的 `value` 和迭代次數 `i`,
        // 然後將 `func` 函式返回的新值賦給 `value`,以便在下一次迭代中使用。
        value = func(value, i)
      }
      // 迴圈結束後,返回最終的迭代值 `value`。
      return value
    }
    
    • 方法流程說明:
      1. 接收 countfuncinitValue 作為引數。
      2. 初始化變數 valueinitValue
      3. 進行 count 次迭代,每次迭代中呼叫 func 函式,傳入當前的 value 和迭代次數 i
      4. func 函式返回一個新值,這個新值成為下一次迭代的 value
      5. 迭代完成後,返回最後一次 func 函式呼叫的結果。

last:輸出陣列的最後一項,如果陣列為空則輸出傳入的預設值

  1. 使用說明

    • 引數:目標陣列,或者空陣列和預設值。
    • 返回值:陣列最後一項,如果陣列為空則輸出傳入的預設值。
  2. 使用程式碼示例

    import { last } from 'radash'
    
    const fish = ['marlin', 'bass', 'trout']
    
    const lastFish = last(fish) // => 'trout'
    const lastItem = last([], 'bass') // => 'bass'
    
  3. 原始碼解析

    // 定義一個泛型函式 `last`,它接收一個具有隻讀屬性的泛型陣列 `array`,
    // 和一個可選的預設值 `defaultValue`,其型別可以是泛型 `T` 或 `null` 或 `undefined`,預設值為 `undefined`。
    export const last = <T>(
      array: readonly T[],
      defaultValue: T | null | undefined = undefined
    ) => {
      // 如果陣列存在且長度大於0,返回陣列的最後一個元素。
      // 否則,返回提供的預設值 `defaultValue`。
      return array?.length > 0 ? array[array.length - 1] : defaultValue
    }
    
    • 方法流程說明:
      1. 檢查傳入的陣列 array 是否存在且長度大於0。
      2. 如果陣列存在且不為空(長度大於0),則返回陣列的最後一個元素 array[array.length - 1]
      3. 如果陣列不存在或為空,返回 defaultValue
      4. 這個函式對於需要安全地訪問陣列最後一個元素而不丟擲錯誤的情況很有用,特別是在不確定陣列是否為空的情況下。透過提供一個預設值,你可以避免在陣列為空時訪問未定義的索引。如果沒有提供預設值,函式將預設返回 undefined

list:建立包含特定項的陣列

  1. 使用說明
    • 引數:start、end、值,步長。
    • 返回值:從start開始遍歷到end,輸出一個陣列,包含特定項(值)的陣列。
  2. 使用程式碼示例
    import { list } from 'radash'
    
    list(3)                  // [0, 1, 2, 3]
    list(0, 3)               // [0, 1, 2, 3]
    list(0, 3, 'y')          // [y, y, y, y]
    list(0, 3, () => 'y')    // [y, y, y, y]
    list(0, 3, i => i)       // [0, 1, 2, 3]
    list(0, 3, i => `y${i}`) // [y0, y1, y2, y3]
    list(0, 3, obj)          // [obj, obj, obj, obj]
    list(0, 6, i => i, 2)    // [0, 2, 4, 6]
    
  3. 原始碼解析
    // 定義一個泛型函式 `list`,它接受一個預設型別引數 `T`,預設為 `number`。
    // 函式接受四個引數:起始值或長度 `startOrLength`,可選的結束值 `end`,
    // 可選的值或對映函式 `valueOrMapper` 用於生成陣列中的值,以及可選的步長 `step`。
    export const list = <T = number>(
      startOrLength: number,
      end?: number,
      valueOrMapper?: T | ((i: number) => T),
      step?: number
    ): T[] => {
      // 使用 `Array.from` 方法來建立一個陣列,它接受 `range` 生成器函式作為引數。
      // `range` 函式根據提供的引數生成一個序列的值。
      return Array.from(range(startOrLength, end, valueOrMapper, step))
    }
    
    • 方法流程說明:
      1. 呼叫 range 函式,傳入 startOrLength(起始值或長度)、end(結束值)、valueOrMapper(值或對映函式)、step(步長)四個引數。這些引數都是可選的,除了 startOrLength 必須提供。
      2. range 函式是一個生成器,根據提供的引數生成一個數字序列。如果指定了 end,那麼 startOrLength 作為起始值,end 作為結束值。如果沒有指定 endstartOrLength 作為序列的長度。valueOrMapper 可以是一個值,此時序列中的每個元素都是這個值;也可以是一個函式,此時序列中的每個元素都是這個函式的返回值。step 指定了序列中每個元素之間的間隔。
      3. Array.from 方法用於從 range 生成器建立一個陣列。生成器的每次迭代返回的值都會成為陣列中的一個元素。
      4. 最終,list 函式返回這個陣列。

max:獲取物件陣列中指定識別符號最大的項

  1. 使用說明
    • 引數:目標物件陣列、指定識別符號的回撥函式。
    • 返回值:符合條件的物件。
  2. 使用程式碼示例
    import { max } from 'radash'
    
    const fish = [
      {
        name: 'Marlin',
        weight: 105,
        source: 'ocean'
      },
      {
        name: 'Bass',
        weight: 8,
        source: 'lake'
      },
      {
        name: 'Trout',
        weight: 13,
        source: 'lake'
      }
    ]
    
    max(fish, f => f.weight) // => {name: "Marlin", weight: 105, source: "ocean"}
    
  3. 原始碼解析
    // 定義一個泛型函式 `max`,它接受一個具有隻讀屬性的泛型陣列 `array`,
    // 以及一個可選的函式 `getter`,該函式用於從陣列元素中提取一個數字用於比較大小。
    export function max<T>(
      array: readonly T[],
      getter?: (item: T) => number
    ): T | null {
      // 如果 `getter` 函式未提供,則使用預設函式,它將元素作為其自己的值。
      const get = getter ?? ((v: any) => v)
      // 呼叫 `boil` 函式,傳入陣列和一個比較函式,
      // 比較函式用 `get` 函式獲取的值來決定哪個元素更大。
      return boil(array, (a, b) => (get(a) > get(b) ? a : b))
    }
    
    // 定義一個泛型函式 `boil`,它接受一個具有隻讀屬性的泛型陣列 `array`,
    // 以及一個比較函式 `compareFunc`,該函式用於比較兩個元素並返回其中一個。
    export const boil = <T>(
      array: readonly T[],
      compareFunc: (a: T, b: T) => T
    ) => {
      // 如果傳入的陣列不存在,或者其長度為0,則返回 null。
      if (!array || (array.length ?? 0) === 0) return null
      // 使用陣列的 `reduce` 方法應用 `compareFunc`,將陣列歸約為單一的值。
      return array.reduce(compareFunc)
    }
    
    • 方法流程說明:
      1. 接收一個陣列 array 和一個可選的 getter 函式。如果 getter 函式提供,它將用於從每個元素中提取用於比較的數字值。
      2. 如果沒有提供 getter 函式,使用一個預設的函式,這個函式簡單地返回元素本身作為比較值。
      3. 呼叫 boil 函式,傳入陣列和一個比較函式。這個比較函式使用 get 函式從兩個元素 ab 中提取值,並返回較大值對應的元素。
      4. boil 函式透過 reduce 方法遍歷陣列,應用比較函式,並最終返回單一的元素,即陣列中的最大元素。如果陣列為空或未定義,boil 函式返回 null
      5. max 函式最終返回 boil 函式的結果,即陣列中的最大元素,如果陣列為空,則返回 null

merge:合併陣列中符合條件的項,並且會覆蓋第一個陣列

  1. 使用說明

    • 引數:陣列1、陣列2、條件函式。
    • 返回值:合併覆蓋後的陣列。
  2. 使用程式碼示例

    import { merge } from 'radash'
    
    const gods = [
      {
        name: 'Zeus',
        power: 92
      },
      {
        name: 'Ra',
        power: 97
      }
    ]
    
    const newGods = [
      {
        name: 'Zeus',
        power: 100
      }
    ]
    
    merge(gods, newGods, f => f.name) // => [{name: "Zeus" power: 100}, {name: "Ra", power: 97}]
    
  3. 原始碼解析

    export const merge = <T>(
      root: readonly T[],
      others: readonly T[],
      matcher: (item: T) => any
    ) => {
      if (!others && !root) return []
      if (!others) return root
      if (!root) return []
      if (!matcher) return root
      return root.reduce((acc, r) => {
        const matched = others.find(o => matcher(r) === matcher(o))
        if (matched) acc.push(matched)
        else acc.push(r)
        return acc
      }, [] as T[])
    }
    
    • 方法流程說明:
      1. 進行一系列的檢查,如果 othersroot 兩個陣列都不存在或為空,或者沒有提供 matcher 函式,就返回 root 或者一個空陣列。
      2. 使用 root 陣列的 reduce 方法構建最終的合併陣列。在每次迭代中,使用 matcher 函式檢查 root 陣列中的當前元素是否在 others 陣列中有匹配的元素。
      3. 如果在 others 陣列中找到了一個匹配的元素,就把這個匹配的元素新增到累加器陣列 acc 中。
      4. 如果沒有找到匹配的元素,就把 root 陣列中的當前元素新增到累加器 acc 中。
      5. 繼續處理 root 陣列的下一個元素,直到所有元素都被處理完畢。
      6. 返回累加器 acc,它現在包含了合併後的元素。

min:獲取物件陣列中指定識別符號最小的項

  1. 使用說明
    • 引數:目標物件陣列、指定識別符號的條件函式。
    • 返回值:符合條件的的項
  2. 使用程式碼示例
    import { min } from 'radash'
    
    const fish = [
      {
        name: 'Marlin',
        weight: 105,
        source: 'ocean'
      },
      {
        name: 'Bass',
        weight: 8,
        source: 'lake'
      },
      {
        name: 'Trout',
        weight: 13,
        source: 'lake'
      }
    ]
    
    min(fish, f => f.weight) // => {name: "Bass", weight: 8, source: "lake"}
    
  3. 原始碼解析
    // 這是 `min` 函式的第一種宣告,它要求傳遞一個 `getter` 函式。
    export function min<T>(
      array: readonly T[],
      getter: (item: T) => number
    ): T | null
    
    // 這是 `min` 函式的第二種宣告,它允許 `getter` 函式是可選的。
    export function min<T>(
      array: readonly T[],
      getter?: (item: T) => number
    ): T | null {
      // 如果沒有提供 `getter` 函式,使用預設函式,它將元素作為其自己的值。
      const get = getter ?? ((v: any) => v)
      // 呼叫 `boil` 函式,傳入陣列和一個比較函式,
      // 比較函式用 `get` 函式獲取的值來決定哪個元素更小。
      return boil(array, (a, b) => (get(a) < get(b) ? a : b))
    }
    
    • 方法流程說明:
      1. 函式接收一個陣列 array 和一個可選的 getter 函式。如果提供了 getter 函式,它將用於從每個元素中提取用於比較的數字值。
      2. 如果沒有提供 getter 函式,則使用一個預設的函式,這個函式簡單地返回元素本身作為比較值。
      3. 呼叫 boil 函式,傳入陣列和一個比較函式。這個比較函式使用 get 函式從兩個元素 ab 中提取值,並返回較小值對應的元素。
      4. boil 函式透過 reduce 方法遍歷陣列,應用比較函式,並最終返回單一的元素,即陣列中的最小元素。如果陣列為空或未定義,boil 函式返回 null
      5. min 函式最終返回 boil 函式的結果,即陣列中的最小元素,如果陣列為空,則返回 null

objectify:根據函式對映的鍵與值把陣列轉換為字典物件

  1. 使用說明
    • 引數:目標物件陣列、條件函式1用於提取鍵、[條件函式2用於提取值]
    • 返回值:字典物件
  2. 使用程式碼示例
    import { objectify } from 'radash'
    
    const fish = [
      {
        name: 'Marlin',
        weight: 105
      },
      {
        name: 'Bass',
        weight: 8
      },
      {
        name: 'Trout',
        weight: 13
      }
    ]
    
    objectify(fish, f => f.name) // => { Marlin: [marlin object], Bass: [bass object], ... }
    objectify(
      fish,
      f => f.name,
      f => f.weight
    ) // => { Marlin: 105, Bass: 8, Trout: 13 }
    
  3. 原始碼解析
    // 定義一個泛型函式 `objectify`,它接受三個引數:
    // `array` 是一個具有隻讀屬性的泛型陣列,
    // `getKey` 是一個函式,用於從陣列元素中提取鍵,
    // `getValue` 是一個可選的函式,用於從陣列元素中提取值,預設情況下,它會返回元素本身作為值。
    export const objectify = <T, Key extends string | number | symbol, Value = T>(
      array: readonly T[],
      getKey: (item: T) => Key,
      getValue: (item: T) => Value = item => item as unknown as Value
    ): Record<Key, Value> => {
      // 使用陣列的 `reduce` 方法來累積一個物件,該物件將陣列元素對映為鍵值對。
      return array.reduce((acc, item) => {
        // 使用 `getKey` 函式從當前元素 `item` 中提取鍵,並使用 `getValue` 函式提取值。
        // 將這個鍵值對新增到累加器物件 `acc` 中。
        acc[getKey(item)] = getValue(item)
        // 返回更新後的累加器 `acc`。
        return acc
      }, {} as Record<Key, Value>) // 初始化累加器 `acc` 為一個空物件。
    }
    
    • 方法流程說明:
      1. 函式接收一個陣列 array,一個 getKey 函式用於提取每個元素的鍵,以及一個可選的 getValue 函式用於提取每個元素的值。
      2. 如果沒有提供 getValue 函式,則使用一個預設的函式,這個函式簡單地將元素本身作為值。
      3. 使用 reduce 方法遍歷陣列。reduce 方法的累加器 acc 是一個物件,用於儲存鍵值對。
      4. 對於陣列中的每個元素 item,使用 getKey 函式提取鍵,並使用 getValue 函式提取值。
      5. 將提取的鍵和值作為一個鍵值對新增到累加器物件 acc 中。
      6. 繼續處理陣列的下一個元素,直到所有元素都被處理完畢。
      7. 返回累加器 acc,它現在是一個完整的物件,包含了從陣列元素對映而來的鍵值對。

range:根據步長生成一個數值範圍內的迭代值

  1. 使用說明
    • 引數:起始值、[結束值]、[迭代函式]、步長。
    • 返回值:用在for迴圈中迴圈輸出處理的結果值。
  2. 使用程式碼示例
    import { range } from 'radash'
    
    range(3)                  // yields 0, 1, 2, 3
    range(0, 3)               // yields 0, 1, 2, 3
    range(0, 3, 'y')          // yields y, y, y, y
    range(0, 3, () => 'y')    // yields y, y, y, y
    range(0, 3, i => i)       // yields 0, 1, 2, 3
    range(0, 3, i => `y${i}`) // yields y0, y1, y2, y3
    range(0, 3, obj)          // yields obj, obj, obj, obj
    range(0, 6, i => i, 2)    // yields 0, 2, 4, 6
    
    for (const i of range(0, 200, 10)) {
      console.log(i) // => 0, 10, 20, 30 ... 190, 200
    }
    
    for (const i of range(0, 5)) {
      console.log(i) // => 0, 1, 2, 3, 4, 5
    }
    
  3. 原始碼解析
    // 定義一個泛型生成器函式 `range`,它接受一個預設型別引數 `T`,預設為 `number`。
    // 函式接受四個引數:起始值或長度 `startOrLength`,可選的結束值 `end`,
    // 可選的值或對映函式 `valueOrMapper` 用於生成序列中的值,以及可選的步長 `step`,預設為 1。
    export function* range<T = number>(
      startOrLength: number,
      end?: number,
      valueOrMapper: T | ((i: number) => T) = i => i as T,
      step: number = 1
    ): Generator<T> {
      // 確定 `valueOrMapper` 是一個函式還是一個固定值。如果是函式,則直接使用;如果不是,則建立一個總是返回該值的函式。
      const mapper = isFunction(valueOrMapper) ? valueOrMapper : () => valueOrMapper
      // 如果提供了 `end` 值,則 `start` 為 `startOrLength`;如果沒有提供 `end` 值,則 `start` 預設為 0。
      const start = end ? startOrLength : 0
      // `final` 是序列的結束值,如果提供了 `end` 值,則使用它;如果沒有,則 `final` 為 `startOrLength`。
      const final = end ?? startOrLength
      // 從 `start` 開始,到 `final` 結束,每次迭代增加 `step`。
      for (let i = start; i <= final; i += step) {
        // 使用 `yield` 關鍵字產生由 `mapper` 函式處理的當前迭代值 `i`。
        yield mapper(i)
        // 如果下一次增加步長後的值會超過 `final`,則提前終止迴圈。
        if (i + step > final) break
      }
    }
    
    • 方法流程說明:
      1. 確定 valueOrMapper 引數是一個函式還是一個值。如果是函式,直接用作對映器;如果是值,建立一個總是返回該值的函式作為對映器。
      2. 確定起始值 start。如果提供了 end 引數,startstartOrLength;否則 start 預設為 0。
      3. 確定結束值 final。如果提供了 end 引數,finalend;否則 finalstartOrLength
      4. 使用一個 for 迴圈從 start 遍歷到 final,每次迭代增加步長 step
      5. 在每次迭代中,使用 yield 關鍵字來產出 mapper 函式處理過的當前值。
      6. 如果在下一次迭代步長加上當前值 i 會超過 final,則提前退出迴圈。
      7. 提示:這個 range 函式提供了類似於 Python 中的 range 函式的功能,允許在迭代中產生一系列的值。透過使用生成器,我們可以惰性地產生這些值,這意味著直到迭代器被消費時,這些值才會被計算和產生。這在處理大範圍的值時非常有用,因為它不需要一次性將所有值載入到記憶體中。

replaceOrAppend:替換物件陣列中的項或是追加項(條件函式不滿足時追加);

  1. 使用說明

    • 引數:被替換陣列、用來的替換的陣列、條件函式。
    • 返回值:替換或者追加後的陣列。
  2. 使用程式碼示例

    import { replaceOrAppend } from 'radash'
    
    const fish = [
      {
        name: 'Marlin',
        weight: 105
      },
      {
        name: 'Salmon',
        weight: 19
      },
      {
        name: 'Trout',
        weight: 13
      }
    ]
    
    const salmon = {
      name: 'Salmon',
      weight: 22
    }
    
    const sockeye = {
      name: 'Sockeye',
      weight: 8
    }
    
    replaceOrAppend(fish, salmon, f => f.name === 'Salmon') // => [marlin, salmon (weight:22), trout]
    replaceOrAppend(fish, sockeye, f => f.name === 'Sockeye') // => [marlin, salmon, trout, sockeye]
    
  3. 原始碼解析

    // 定義一個泛型函式 `replaceOrAppend`,它接受三個引數:
    // `list` 是一個具有隻讀屬性的泛型陣列,
    // `newItem` 是一個新的元素,將被新增到陣列中,
    // `match` 是一個函式,用於確定新元素應該替換陣列中的哪個現有元素。
    export const replaceOrAppend = <T>(
      list: readonly T[],
      newItem: T,
      match: (a: T, idx: number) => boolean
    ) => {
      // 如果 `list` 和 `newItem` 都不存在或為空,則返回一個空陣列。
      if (!list && !newItem) return []
      // 如果 `newItem` 不存在或為空,則返回 `list` 陣列的副本。
      if (!newItem) return [...list]
      // 如果 `list` 不存在或為空,則返回一個只包含 `newItem` 的陣列。
      if (!list) return [newItem]
      // 遍歷 `list` 陣列,尋找一個匹配的元素。
      for (let idx = 0; idx < list.length; idx++) {
        const item = list[idx]
        // 如果 `match` 函式返回 `true`,則在該位置替換元素。
        if (match(item, idx)) {
          // 建立一個新陣列,其中包含從 `list` 開始到匹配位置之前的所有元素,
          // 然後是 `newItem`,然後是從匹配位置之後到 `list` 結束的所有元素。
          return [
            ...list.slice(0, idx),
            newItem,
            ...list.slice(idx + 1, list.length)
          ]
        }
      }
      // 如果沒有找到匹配的元素,將 `newItem` 追加到 `list` 陣列的末尾。
      return [...list, newItem]
    }
    
    • 方法流程說明:
      1. 進行一系列的檢查,如果 listnewItem 都不存在或為空,或者只有 newItem 不存在或為空,或者只有 list 不存在或為空,則返回相應的結果。
      2. 如果 listnewItem 都存在,遍歷 list 陣列,對於每個元素和它的索引,呼叫 match 函式。
      3. 如果 match 函式對某個元素返回 true,說明找到了應該被替換的元素。函式將建立一個新陣列,該陣列由以下部分組成:從 list 的開始到匹配元素之前的部分,newItem,以及從匹配元素之後到 list 結尾的部分。
      4. 如果遍歷完成後沒有找到任何匹配的元素,函式將 newItem 新增到 list 的末尾,並返回新陣列。

下期我們將介紹 radash 中剩餘的陣列相關方法

  • replace :查詢指定項,並用傳入的去替換;
  • select :對陣列同時進行過濾和對映,輸出對映後的value陣列;
  • shift :把目標陣列向右移動 n 個位置返回為一個新陣列;
  • sift:過濾調列表中值為false的項,返回剩餘為true的項組成的陣列;
  • sort :把陣列按照條件函式指定的項的數值大小排序,支援升序和降序;
  • sum:陣列物件根據條件函式指定想的陣列求和;
  • toggle:查詢陣列中是否有我們給定的項,有則刪除,沒有則新增;
  • unique:陣列去重,去除陣列中重複的項;
  • zipToObject:將第一個陣列中的鍵對映到第二個陣列中對應的值;
  • zip:把兩個陣列變為二維陣列,二維陣列中的每個陣列包含兩個項分別為兩個傳入陣列的相同位置的項。

寫在後面

  • 後續作者會整理一份radash完整方法目錄上傳,方便沒法訪問外網的朋友檢視使用。
  • 下期方法分享完後,會同步整理分享Array方法的使用說明目錄,方便大家查閱對照使用。
  • 小tips:不出意外的話博主每週五會定時更新,出意外的話就是出意外了。
  • 大家有任何問題或見解,歡迎評論區留言交流和批評指正!!!
  • 點選訪問:radash官網

相關文章