如何在JavaScript中实现Map的computeIfAbsent/getOrElseUpdate功能?
如何在JavaScript/TypeScript中实现Map的"获取或更新"逻辑?
问题背景
假设存在以下条件:
- m 是一个
Map<number, V>(V为任意类型) - k 是一个 number 类型值
需要编写一个表达式,实现以下效果:
- 要么获取键k对应的已存在的V类型值,
- 要么创建一个新的v:V,将其存入Map的键k下,并返回该v
例如,SOME_EXPR(m, k, []) 需要在 m.get(k) 存在时返回该值,否则执行 m.set(k, []) 并返回 []。
具体示例
假设要逐步构建一个 Map<number, number[]>,为键48添加值100和200,为键52添加值300,按需创建空数组。需要类似 SOME_EXPR(map, key, value) 的表达式,使得执行:
var m = new Map(); // Map<number, number[]> SOME_EXPR(m, 48, []).push(100) SOME_EXPR(m, 48, []).push(200) SOME_EXPR(m, 52, []).push(300)
后,Map变为:
{ 48 -> [100, 200]; 52 -> [300] }
那么应该用什么替代 SOME_EXPR?
已尝试方案
辅助函数方案
可以创建一个辅助方法:
function getOrElseUpdate(m, k, defaultValue) { if (!m.has(k)) { m.set(k, defaultValue); } return m.get(k); }
然后用 getOrElseUpdate(m, k, []) 替代 SOME_EXPR,但该方法需要三次计算哈希值,且维护时需跳转查看函数定义,不够直观。
内联表达式方案
尝试内联实现:
SOME_EXPR(m,k,v) := ((k) => (m.get(k) || ((v) => (m.set(k, v), v))(v)))(k)
对应示例代码变为:
var m = new Map(); ((k) => (m.get(k) || ((v) => (m.set(k, v), v))([])))(42).push(100); ((k) => (m.get(k) || ((v) => (m.set(k, v), v))([])))(42).push(200); ((k) => (m.get(k) || ((v) => (m.set(k, v), v))([])))(58).push(300);
该方案可行,但写法怪异,可读性差。
现在希望找到符合JavaScript惯用写法的实现,最好能在TypeScript中良好类型化。
其他语言的参考实现
Scala
val m = HashMap.empty[Int, ListBuffer[Int]] m.getOrElseUpdate(48, ListBuffer.empty) += 100 m.getOrElseUpdate(48, ListBuffer.empty) += 200 m.getOrElseUpdate(52, ListBuffer.empty) += 300 // m 现在为: // // HashMap( // 48 -> ListBuffer(100, 200), // 52 -> ListBuffer(300) // )
Java
HashMap<Integer, List<Integer>> m = new HashMap<>(); m.computeIfAbsent(42, k -> new LinkedList<>()).add(100); m.computeIfAbsent(42, k -> new LinkedList<>()).add(200); m.computeIfAbsent(58, k -> new LinkedList<>()).add(300); // m = {58=[300], 42=[100, 200]}
内容的提问来源于stack exchange,提问作者Andrey Tyukin
相关产品推荐
相关产品推荐

