簡體   English   中英

如何使用 TypeScript/JavaScript 中的函數式編程將元素推送到 Map 內的數組?

[英]How to push an element to an array inside a Map using functional programming in TypeScript/JavaScript?

我剛剛開始從 OOP 背景到學習 FP,以及從編寫普通 TypeScript(命令式?)遷移到功能性 TypeScript 代碼的過程。 不幸的是,我已經在努力弄清楚如何將其更改為功能代碼:

const foos: Map<
  string,
  Bar[]
> = new Map();

export const addBar = (
  key: string,
  bar: Bar
) => {
  const foo = foos.get(key);

  if (foo) {
    foo.push(bar);
  } else {
    foos.set(key, [bar]);
  }
};

我了解 .map .filter .concat 可以如何用於數組,但是如何處理包含數組的 Map?

關於 foos Map 它我猜 Map 本身需要只讀,還有里面的 Bar 數組,所以 .set .push 是不可能的。 但是如果我不能在 Map 上調用 .set 因為它是只讀的,那么使用 Map 還是我應該只使用一個對象是否有意義?

如果沒有可變性,如何將元素推送到 Map 值內的數組(或者,如果鍵不存在,則使用該數組創建一個新映射,就像上面的代碼一樣)?

並且這種性能是否足夠高,因為我需要每隔幾秒向數組添加一個新元素,每次發生更改時復制整個地圖(包括其許多數組)的不可變方式的性能不會比我差很多嗎?剛剛像通常那樣改變了數組?

您根本無法使用本機Map因為它僅提供命令式接口。

你可以找到一個開源庫,比如流行的 ImmutableJS。

或者您可以編寫自己的持久(不可變)數據結構。 基本要求是您的數據結構提供的操作不會修改輸入。 相反,每個操作都會返回一個新的數據結構 -

const PersistentMap =
  { create: () =>
      ({})
  , set: (t = {}, key, value) =>
      ({ ...t, [key]: value })      // <-- immutable operation
  }

我們先看一個empty map,一個set操作的結果,然后確定這個empty map沒有被修改——

const empty =
  PersistentMap.create()

console.log
  ( empty
  , PersistentMap.set(empty, "hello", "world")
  , empty
  )

// {}
// { hello: "world" }
// {}

現在讓我們看看一個新的中間狀態m1 每次我們看到set返回一個新的持久化映射並且不會修改輸入——

const m1 =
  PersistentMap.set(empty, "hello", "earth")

console.log
  ( m1
  , PersistentMap.set(m1, "stay", "inside")
  , m1
  )
// { hello: "earth" }
// { hello: "earth", stay: "inside" }
// { hello: "earth" }

現在回答您的問題,我們可以向我們的PersitentMap添加push操作 - 我們只需要確保我們不修改輸入。 這是一種可能的實現 -

const PersistentMap =
  { // ...

  , push: (t = {}, key, value) =>
      PersistentMap.set            // <-- immutable operation
        ( t
        , key
        , Array.isArray(t[key])
            ? [ ...t[key], value ] // <-- immutable operation
            : [ value ]
        )
  }

我們在下面看到push行動。 請注意,結果更改了m2empty -

const m2 =
  PersistentMap.push(empty, "fruits", "apple")

console.log
  ( m2
  , PersistentMap.push(m2, "fruits", "peach")
  , m2
  , empty
  )

// { fruits: [ "apple" ] }
// { fruits: [ "apple", "peach" ] }
// { fruits: [ "apple" ] }
// {}

展開下面的代碼段以在您自己的瀏覽器中驗證結果

 const PersistentMap = { create: () => ({}) , set: (t = {}, key, value) => ({ ...t, [key]: value }) , push: (t = {}, key, value) => PersistentMap.set ( t , key , Array.isArray(t[key]) ? [ ...t[key], value ] : [ value ] ) } const empty = PersistentMap.create() console.log ( empty , PersistentMap.set(empty, "hello", "world") , empty ) // {} // { hello: "world" } // {} const m1 = PersistentMap.set(empty, "hello", "earth") console.log ( m1 , PersistentMap.set(m1, "stay", "inside") , m1 ) // { hello: "earth" } // { hello: "earth", stay: "inside" } // { hello: "earth" } const m2 = PersistentMap.push(empty, "fruits", "apple") console.log ( m2 , PersistentMap.push(m2, "fruits", "peach") , m2 , empty ) // { fruits: [ "apple" ] } // { fruits: [ "apple", "peach" ] } // { fruits: [ "apple" ] } // {}

我認為這取決於您想要實現的目標。 如果您希望您的代碼可測試,FP 並不總是意味着只編寫函數,您仍然可以使用類,但是如果您有一段復雜的代碼要單獨測試,則可以導出該代碼段進行測試,並且它看起來像這樣:

// types.ts
type FooDis = Record<string, object[]>;

// addBarToFoos.ts
export const addBarToFoos = (foos: FooDis) => (key: string, bar: object): FooDis {
  foos = {
    ...foos,
    [key]: [
      ...foos[key],
      bar
    ]
  };

  return foos;
}

// FooClass.ts 
export class FooClass {
  private foos: FooDis = {};

  addBar(key: string, bar: object) {
    this.foos = addBarToFoos(this.foos)(key, bar);
  }
}

這樣,“復雜”方法無需外部依賴即可單獨測試,並且您有一個使用該方法的實現。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM