2015-02-07 19 views
7

我知道有关类似的问题。他们不帮助我 - 如果没有现有密钥,代码不起作用。不错的方式来添加数字元素在斯卡拉地图如果密钥存在或插入新的元素它不是

我只需要一些很好的方法来将Map添加到现有的键(如果它存在)或将NEW键(如果map不包含适当的键)添加到现有的键。

下面的代码工作,但我不喜欢它:

val a = collection.mutable.Map(("k1" -> 1), ("k2" -> 5)) 
val key = "k1" 

val elem = a.get(key) 
if (elem == None) { 
    a += ("k5" -> 200) 
} else { 
    a.update(key, elem.get + 5) 
} 

任何一点更好的? 当前的Scala版本是2.10.4,我目前无法切换到2.11。 可变映射不是100%的限制,但是是首选。

这里是,例如,similar question,但我也需要考虑不存在的钥匙的情况。至少我们应该了解a.get(key)可能是None或者添加一些更好的方法。好主意是|+|但我想保持基本的Scala 2.10.x.

回答

6

您可以创建你自己的函数,该函数的目的:

def addOrUpdate[K, V](m: collection.mutable.Map[K, V], k: K, kv: (K, V), 
         f: V => V) { 
    m.get(k) match { 
    case Some(e) => m.update(k, f(e)) 
    case None => m += kv 
    } 
} 

addOrUpdate(a, "k1", "k5" -> 200, (v: Int) => v + 5) 
+1

可能现在它是最好的选择。但是为什么Scala复杂的容器库不能提供开箱即用的简单东西 - 使用key和value可以增加值(如果不存在任何键)或者使用当前值和我的值都可以做些什么?下次肯定会把这个至少包括到公司的图书馆(魔鬼)中。 – 2015-02-07 21:16:52

+5

这是非常奇特的恕我直言:如果一个**其他**不存在,添加一个键不是一个真正的主流行为 – 2015-02-07 21:21:47

+1

为什么?例如,我想通过key来形成聚合地图,我需要逐个添加元素,所以像'reduce()'这样的东西不能被使用? – 2015-02-07 21:32:14

7

做到这一点的最简单的办法:

a += a.get(key).map(x => key -> (x + 5)).getOrElse("k5" -> 200) 

一般:

a += a.get(k).map(f).map(k -> _).getOrElse(kv) 

相同的,如果你的字典不可变:

m + m.get(k).map(f).map(k -> _).getOrElse(kv) 

所以我没有看到任何理由在这里使用可变集合。

如果你不喜欢所有这些Option.map事情:

m + (if (m.contains(k)) k -> f(m(k)) else kv) 

注意,有一整类可能的变化:

k1 -> f(m(k1)) else k2 -> v2 //original 
k1 -> f(m(k1)) else k1 -> v2 
k1 -> f(m(k2)) else k2 -> v2 
k1 -> f(m(k2)) else k1 -> v2 
k2 -> v2 else k1 -> f(m(k1)) 
k1 -> v2 else k1 -> f(m(k1)) 
k2 -> v2 else k1 -> f(m(k2)) 
k1 -> v2 else k1 -> f(m(k2)) 
... //v2 may also be a function from some key's value 

那么,为什么它不是一个标准的功能?国际海事组织,因为所有的变化仍然可以作为单线实施。如果你想拥有所有功能的图书馆,可以实施为单行,你知道,这是斯卡拉:)。

P.S.如果宇还奇怪为什么没有“更新(d)如果坚持”功能 - 见@Rex克尔的回答here

1

一个莫名其妙清晰的方式来做到这一点:

val a = collection.mutable.Map[String, Int]() withDefault insertNewValue 

def insertNewValue(key: String): Int = 
    a += key -> getValueForKey(key) 
    a(key) 
} 

def getValueForKey(key: String): Int = key.length 

不过,我完全disencourage使用可变集合。最好将内部可变状态保持为包含不可变字段的变量。

这是因为一个简单的规则,除非完全有必要,否则不应暴露自己的内部状态,如果这样做,应该尽可能减少可能产生的潜在副作用。

如果您公开一个可变状态的引用,任何其他actor可以更改它的值,从而失去参考透明度。所有对可变集合的引用都很长,并且很难使用,这并非偶然。这是一个开发人员给你的信息

毫不奇怪,代码仍然保持相同,在地图实例化时有一些最小的变化。

var a = Map[String, Int]() withDefault insertNewValue 

def insertNewValue(key: String): Int = { 
    a += key -> getValueForKey(key) 
    a(key) 
} 

def getValueForKey(key: String): Int = key.length 
相关问题