如果密钥存在,则在Scala映射中向元素添加数字的好方法或不插入新元素

Rom*_*nko 13 containers dictionary scala

我知道几个类似的问题.它们对我没有帮助 - 如果没有现有密钥,代码就不起作用.

我只需要一些很好的方法来附加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)
}
Run Code Online (Sandbox Code Playgroud)

有没有更好的一点?当前的Scala版本是2.10.4,我目前无法切换到2.11.可变图不是100%限制,而是优选的.

例如,这是类似的问题,但我还需要考虑不存在的那个不存在的密钥的情况.至少我们应该理解a.get(key)可能是None或添加一些更好的方法.好主意|+|但是我想保留基本的Scala 2.10.x.

dk1*_*k14 14

最简单的方法:

a += a.get(key).map(x => key -> (x + 5)).getOrElse("k5" -> 200)
Run Code Online (Sandbox Code Playgroud)

一般来说:

a += a.get(k).map(f).map(k -> _).getOrElse(kv)
Run Code Online (Sandbox Code Playgroud)

如果您的字典是不可变的,则相同:

m + m.get(k).map(f).map(k -> _).getOrElse(kv)
Run Code Online (Sandbox Code Playgroud)

所以我认为没有理由在这里使用可变集合.

如果你不喜欢这些Option.map东西:

m + (if (m.contains(k)) k -> f(m(k)) else kv)
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

那么,为什么它不是标准功能呢?IMO,因为所有变体仍然可以实现为单行.如果你想要具有所有功能的库,可以实现为单行,你知道,它是Scalaz :).

PS如果你也想知道为什么没有"更新(d)if persist"功能 - 请参阅@Rex Kerr 这里的答案


Jea*_*art 13

您可以为此创建自己的功能:

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)
Run Code Online (Sandbox Code Playgroud)

  • 这是非常奇怪的恕我直言:如果**其他**不存在,添加一个键并不是真正的主流行为 (5认同)
  • 可能现在它是最好的选择.但是为什么Scala复杂的容器库不能提供开箱即用的简单东西 - 键和值要么增加值(如果没有键存在),要么用当前值和我的那个做一些事情?下次肯定会将此至少包括在公司图书馆(魔鬼)中. (3认同)
  • 为什么?例如,我想通过键作为map来形成聚合,并且我需要一个接一个地添加元素,因此无法使用诸如reduce()之类的东西? (2认同)

小智 6

Scala 2.13 引入了updatedWith方法,这似乎是根据键的存在条件更新映射的最惯用的方法。

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)
Run Code Online (Sandbox Code Playgroud)

也可以使用它删除值:

val a2 = a.updatedWith("k2") { 
  case Some(5) => None
  case v => v 
}
println(a2) // Map(k1 -> 1)
Run Code Online (Sandbox Code Playgroud)

Scala 标准库参考的摘录:

def updatedWith[V1 >: V](key: K)(remappingFunction: (Option[V]) => Option[V1]): Map[K, V1]

更新指定键及其当前可选映射值的映射(Some如果有当前映射,None如果没有)。

如果重映射函数返回Some(v),则使用新值更新映射v。如果重映射函数返回None,则删除映射(如果最初不存在,则保持不存在)。如果函数本身抛出异常,则重新抛出异常,当前映射保持不变。