为了账号安全,请及时绑定邮箱和手机立即绑定

如何使用 TypeScript/JavaScript 中的函数式编程将元素推送到 Map 内的数组?

如何使用 TypeScript/JavaScript 中的函数式编程将元素推送到 Map 内的数组?

哈士奇WWW 2022-06-09 16:53:16
我刚刚开始了从 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 值内的数组(或者如果键不存在,则使用数组创建一个新映射,如上面的代码中所示)?这是否足够高效,因为我需要每隔几秒向数组添加一个新元素,每次发生更改时复制整个地图(包括其许多数组)的不可变方式不会比我执行得更糟吗?刚刚像您通常所做的那样对数组进行了变异?
查看完整描述

2 回答

?
FFIVE

TA贡献1797条经验 获得超6个赞

您根本无法使用本机Map,因为它仅提供命令式接口。


您可以使用开源库,例如流行的 ImmutableJS。


或者您可以编写自己的持久(不可变)数据结构。基本要求是您的数据结构提供的操作不会修改输入。相反,每个操作都会返回一个新的数据结构 -


const PersistentMap =

  { create: () =>

      ({})

  , set: (t = {}, key, value) =>

      ({ ...t, [key]: value })      // <-- immutable operation

  }

我们首先看一张empty地图,一个set操作的结果,然后确保empty地图没有被修改——


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

现在回答你的问题,我们可以添加一个push操作到我们的PersitentMap- 我们只需要确保我们不修改输入。这是一种可能的实现-


const PersistentMap =

  { // ...


  , push: (t = {}, key, value) =>

      PersistentMap.set            // <-- immutable operation

        ( t

        , key

        , Array.isArray(t[key])

            ? [ ...t[key], value ] // <-- immutable operation

            : [ value ]

        )

  }

我们push在下面看到行动。请注意,m2也不会empty因此而改变 -


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" ] }

// {}


查看完整回答
反对 回复 2022-06-09
?
Cats萌萌

TA贡献1805条经验 获得超9个赞

我认为这取决于你想要达到的目标。如果您希望您的代码是可测试的,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);

  }

}

这样,“复杂”方法可以在没有外部依赖的情况下单独测试,并且您有一个使用该方法的实现。


查看完整回答
反对 回复 2022-06-09
  • 2 回答
  • 0 关注
  • 141 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号