How to push an element to an array inside a Map using functional programming in TypeScriptJavaScript? - Stack Overflow

I just started my journey from a OOP background to learning FP, and the process of migrating from writi

I just started my journey from a OOP background to learning FP, and the process of migrating from writing normal TypeScript (imperative?) to functional TypeScript code. Unfortunately I already struggle with figuring out how to change this into functional code:

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]);
  }
};

I understand how .map .filter .concat can be used on an array, but how to deal with a Map that holds arrays?

Regarding the foos Map it I guess the Map itself needs to be read only, and also the array of Bar inside it, so .set .push is not possible. But if I cannot call .set on the Map because it is read only, does it even make sense to use a Map or should I just use an object?

Without mutability how to push an element to an array inside the Map values (or make a New map with the array if the key does not already exist, like in the code above)?

And is this performant enough, since I will need to add an new element to the array every other seconds, will the immutable way of copying the entire map (including its many arrays) each time a change happens not perform a lot worse than if I had just mutated the array like you'd typically do?

I just started my journey from a OOP background to learning FP, and the process of migrating from writing normal TypeScript (imperative?) to functional TypeScript code. Unfortunately I already struggle with figuring out how to change this into functional code:

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]);
  }
};

I understand how .map .filter .concat can be used on an array, but how to deal with a Map that holds arrays?

Regarding the foos Map it I guess the Map itself needs to be read only, and also the array of Bar inside it, so .set .push is not possible. But if I cannot call .set on the Map because it is read only, does it even make sense to use a Map or should I just use an object?

Without mutability how to push an element to an array inside the Map values (or make a New map with the array if the key does not already exist, like in the code above)?

And is this performant enough, since I will need to add an new element to the array every other seconds, will the immutable way of copying the entire map (including its many arrays) each time a change happens not perform a lot worse than if I had just mutated the array like you'd typically do?

Share Improve this question asked Mar 31, 2020 at 21:59 Dac0d3rDac0d3r 1,8547 gold badges47 silver badges87 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 4

You simply cannot use the native Map because it only provides an imperative interface.

You could reach for an open source library such as the popular ImmutableJS.

Or you could write your own persistent (immutable) data structures. The essential requirement is that the operations provided by your data structure do not modify the inputs. Instead a new data structure is returned with each operation -

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

We first look at an empty map, the result of a set operation, and then make sure that empty map is not modified -

const empty =
  PersistentMap.create()

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

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

Now let's look at a new intermediate state, m1. Each time we see set returns a new persistent map and does not modify the input -

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

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

Now to answer your question, we can add a push operation to our PersitentMap - we only have to ensure we do not modify the input. Here's one possible implementation -

const PersistentMap =
  { // ...

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

We see push in action below. Note that m2 nor empty are changed as a result -

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

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

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

Expand the snippet below to verify the results in your own browser

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

I think it depends on what you want to achieve. If you want your code to be testable, FP, doesn't always mean just writing functions, you can still use classes but if you have a plex piece of code you want to test separately, you can export that piece to test that, and it would look something like that:

// 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);
  }
}

This way, the "plex" method is separately testable without external dependencies, and you have an implementation that uses that method.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745662377a4638914.html

相关推荐

  • C#高性能开发之类型系统:从C# 7.0 到C# 14的类型系统演进全景

    自C# 7.0以来,C#语言在类型系统方面引入了众多新数据类型、类型构造和语言特性,以提升性能、类型安全性和开发效率。本文全面整理了从C# 7.0到C# 14.0(截至2025年4月,C# 14.0为预览版)类型系统的新增内容,包括值元组、

    2小时前
    20
  • JetBrains IDEs GO AI:最新coding agent,更智能的编程助手!

    大家好,欢迎来到程序视点!我是你们的老朋友.小二!JetBrains AI现在,AI 几乎无处不在。作为一名资深码农,我一直期待着有更智能、更高效的工具来提高开发的工作效率,并为开发带来更多乐趣,从而让自己从枯燥的CRUD中解放出来!从 2

    1小时前
    10
  • 更正

    之前文章 地表最强基准-ADR1001 中:这个地方有错误业界流量很高的号,百花潭也转载了,不改正的话变成了错误永流传。有读者留言说,恒温控制晶振(OCXO)也有类似的设计:对于某些应用,TCXO(温度补偿)的频率-温度稳定性指标仍无法满足

    1小时前
    10
  • JUC并发—6.AQS源码分析二

    大纲1.ReentractReadWriteLock的基本原理2.基于AQS实现的ReentractReadWriteLock3.ReentractReadWriteLock如何竞争写锁4.ReentractReadWriteLock如何竞

    1小时前
    10
  • 拿自己的旧电脑搭建了个服务器!

    最近总是想搭建自己的网站,奈何皮夹里空空如也,服务器也租不起,更别说域名了。于是我就寻思能否自己搭建个服务器,还不要钱呢?还真行!!!经过几天的冲浪,我发现有两个免费的建站工具:Apache和Nginx由于两个工具建站方法差不多,所以我就以

    1小时前
    00
  • 我把AI接上了Figma、WhatsApp、浏览器……然后它开始自己动起来了!

    大家好,你有没有幻想过这样一幕:你家的 AI 助手,突然接过你的手机,自己发了条微信。 紧接着,它点开了 Chrome,滑动了几下网页,做了个表单提交。然后它打开了 Figma,开始画 UI 界面。 最后,它还用自己的声音给人打了个电话,说

    1小时前
    00
  • Power BI 展示产品边际贡献

    SQLBI最近发布了一篇动态帕累托的文章,在帕累托曲线图表上增加图例,可以不对不同区域的客户贡献进行横向对比:来源 如果把X轴换成产品,该方案还可用来查看不同品类产品的边际贡献,进而对产品线规划进行改善。上图Computers Acces

    1小时前
    00
  • OFC 2025三菱报告:高速EML的结构设计和封装优化

    一、AI集群发展催生光互连技术需求从市场趋势来看,AI集群对光收发器的需求呈现出强劲的增长态势。AI Scale out网络中光收发器数量持续攀升,同时,预计从2028年起,AI Scale up市场也将迎来爆发 ,这使得光收发器的需求

    1小时前
    00
  • 一个让DevOps癫狂的项目一键部署数百个MCP服务器

    背景MCP(Model Context Protocol)是一种新兴的标准化协议,用于管理大型语言模型(LLM)与外部系统之间的上下文交互。随着 AI 技术的快速发展,越来越多的开发者需要将 LLMs 与各种外部工具、API 和数据源集成。

    1小时前
    00
  • REST API 还是 GraphQL?

    前言说到 API 设计,GraphQL 和 RESTful API 是当前最主流的两种选择,各自有其独特优势与挑战。有幸的是,我上家公司的时候,使用的就是 GraphQL,而在上上家使用的是 RESTful API,今天我就结合我的经验,来

    1小时前
    00
  • 业内首次! 全面复现DeepSeek

    机器之心发布机器之心编辑部OpenAI 的 o1 系列和 DeepSeek-R1 的成功充分证明,大规模强化学习已成为一种极为有效的方法,能够激发大型语言模型(LLM) 的复杂推理行为并显著提升其能力。然而,这些推理模型的核心训练方法在其技

    54分钟前
    00
  • 仅用3周时间,就打造出Manus开源平替!贡献源代码,免费用

    机器之心报道机器之心编辑部AI 不再仅仅是一个工具,而是开始成为一个真正的队友。可以对标 Manus 的智能体 Suna 来了!(有没有发现它是 manus 倒过来写)这款智能体由 Kortix AI 团队打造,开源并且完全免费。image

    53分钟前
    00
  • DeepMind CEO 放话:未来十年赌上视觉智能,挑战 OpenAI 语言统治地位

    整理|冬梅、核子可乐去年成功斩获诺贝尔奖之后,Demis Hassabis 决定与一位国际象棋世界冠军打场扑克以示庆祝。Hassabis 一直痴迷于游戏,这股热情也成为他 AI 先驱之路上的契机与驱力。近日,做客一档名为《60 分钟》的访

    47分钟前
    00
  • 【一步步开发AI运动APP】八、自定义姿态动作识别检测——之姿态相似度比较

    之前我们为您分享了【一步步开发AI运动小程序】开发系列博文,通过该系列博文,很多开发者开发出了很多精美的AI健身、线上运动赛事、AI学生体测、美体、康复锻炼等应用场景的AI运动小程序;为了帮助开发者继续深耕AI运动领域市场,今天开始我们将为

    31分钟前
    00
  • .NET 9版本支持说明

    在深入探讨.NET 9库的激动人心改进前,有必要了解微软对.NET版本的支持策略。• 奇数版本(如.NET 9):属于标准期限支持(STS),提供18个月支持周期,适合尝试前沿功能。• 偶数版本(如.NET 8或未来的.NET 10):提供

    29分钟前
    00
  • 科研人狂喜!不用再到处找文献,这个神器让你轻松获取全网外文文献

    写论文时,你是否也有过这样的困扰?SCI - Hub?Library Genesis?几个数据库反复横跳,还是找不到想要的文献。想查最新的外文研究成果,却不知道该上哪个数据库,在各个平台间来回切换,浪费了大量时间和精力,结果还可能一无所获。

    22分钟前
    00
  • 深入微服务核心:从架构设计到规模化

    《Building Microservices》这本书是吃透微服务的大部头,本文基于全书内容,系统性地阐述了微服务架构的设计原则、实施策略与挑战,从微服务的核心概念出发,延伸到架构设计、服务拆分、集成技术及规模化实践,为开发者提供了构建稳健

    21分钟前
    00
  • linux命令详细说明以及案例

    参考资料linux查看进程linux解压ziplinux详细说明以及案例centos7镜像下载详细说明以及案例centos 8详细说明以及案例centos 安装docker详细说明以及案例HTTP,TCP,UDP常见端口对照表centos

    5分钟前
    00
  • Windows实用设置工具,你想要的系统功能它都有!

    各位系统调校爱好者们注意啦&#xff01;今天给大家安利一款装机必备的Windows实用工具包&#xff0c;堪称系统设置的"后悔药"&#xff0c;操作系统的"美图秀"&am

    2分钟前
    00
  • ComfyUI教程|基础篇:安装方法(Windows系统)

    前言 前言 ComfyUI作为一款功能强大的AI生图工具&#xff0c;它通过节点方式&#xff0c;使用户可以直观地看到各个模块的功能&#xff0c;并根据需求进行调整和连接。这种方法使工作流程更加清晰&

    15秒前
    00

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信