我知道几个类似的问题。他们不帮助我 - 如果没有现有密钥,代码就不起作用。
我只需要一些很好的方法来附加Map,将值添加到现有密钥(如果它确实存在)或将其作为新密钥(如果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。
答案 0 :(得分:13)
最简单的方法:
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
那么,为什么它不是标准功能呢? IMO,因为所有变体仍然可以实现为单行。如果您想要具有所有功能的库,那么可以将其实现为单行,您知道,它是Scalaz :)。
P.S。如果你也想知道为什么没有"更新(d)如果坚持"功能 - 请参阅@Rex Kerr的回答here
答案 1 :(得分:11)
您可以为此目的创建自己的功能:
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)
答案 2 :(得分:5)
Scala 2.13 introduced updatedWith
method似乎是最惯用的方式,可根据密钥的存在有条件地更新地图。
val a = Map(("k1" -> 1), ("k2" -> 5))
val a1 = a.updatedWith("k1") {
case Some(v) => Some(v + 5)
case None => Some(200)
}
println(a1) // Map(k1 -> 6, k2 -> 5)
一个人也可以使用它删除值:
val a2 = a.updatedWith("k2") {
case Some(5) => None
case v => v
}
println(a2) // Map(k1 -> 1)
摘录自Scala Standard Library reference:
def updatedWith[V1 >: V](key: K)(remappingFunction: (Option[V]) => Option[V1]): Map[K, V1]更新指定键及其当前可选映射值的映射(如果存在当前映射,则为
Some
,否则为None
)。如果重新映射函数返回
Some(v)
,则映射将用新值v
更新。如果重新映射函数返回None
,则映射将被删除(如果最初不存在,则保持不存在)。如果函数本身引发异常,则重新引发该异常,并且当前映射保持不变。
答案 3 :(得分: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
但是,我完全不鼓励使用可变集合。最好将内部可变状态保存为包含不可变字段的变量。
这是因为一个简单的规则,你不应该暴露你的内部状态,除非它是完全必要的,如果你这样做,你应该尽可能地减少可能产生的潜在副作用。
如果您公开了一个可变状态的引用,那么任何其他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