Reputation: 13046
I know about couple of similar questions. They don't help me - code does not work if there is no existing key.
I need just some nice approach to append Map with value adding it to existing key (if it does exist) or putting as NEW key (if map does not contain appropriate key).
Following code works but I don't like it:
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)
}
Any point to better one? Current Scala version is 2.10.4 and I cannot currently switch to 2.11. Mutable map is not 100% limitation but preferred.
Here is, for example, similar question but I also need to account case of non-existing key which is not accounted there. At least we should understand a.get(key)
could be None
or add some better approach. Good idea was |+|
but I'd like to keep basic Scala 2.10.x.
Upvotes: 17
Views: 20763
Reputation: 326
For your mutable map, you can just do:
val a = collection.mutable.Map(("k1" -> 1), ("k2" -> 5))
val key = "k1"
a.update(key, a.getOrElse(key, 0) + 5)
a // val res1: scala.collection.mutable.Map[String,Int] = HashMap(k1 -> 6, k2 -> 5)
Upvotes: 1
Reputation: 794
Scala 2.13 introduced updatedWith
method which seems to be the most idiomatic way to update a map conditionally on the existence of the key.
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)
One may also remove values using it:
val a2 = a.updatedWith("k2") {
case Some(5) => None
case v => v
}
println(a2) // Map(k1 -> 1)
An excerpt from the Scala Standard Library reference:
def updatedWith[V1 >: V](key: K)(remappingFunction: (Option[V]) => Option[V1]): Map[K, V1]Update a mapping for the specified key and its current optionally-mapped value (
Some
if there is current mapping,None
if not).If the remapping function returns
Some(v)
, the mapping is updated with the new valuev
. If the remapping function returnsNone
, the mapping is removed (or remains absent if initially absent). If the function itself throws an exception, the exception is rethrown, and the current mapping is left unchanged.
Upvotes: 7
Reputation: 367
One somewhat clear way to do this:
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
Still, I totally discourage the use of mutable collections. It's preferable to keep internal mutable state as variables holding immutable fields.
This is because of a simple rule, you shouldn't expose your internal state unless it's totally necessary, and if you do, you should diminish as much as possible the potential side effects that may produce.
If you expose a reference of a mutable state, any other actor can change it's values, thus losing referential transparency. It's not by chance that all references to mutable collections are quite long, and difficult to use. That's a developer's message for you
Not surprisingly, the code still remains the same, with some minimal changes at the map instantiation.
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
Upvotes: 1
Reputation: 22374
The shortest way to do that:
a += a.get(key).map(x => key -> (x + 5)).getOrElse("k5" -> 200)
In general:
a += a.get(k).map(f).map(k -> _).getOrElse(kv)
Same if your dictionary is immutable:
m + m.get(k).map(f).map(k -> _).getOrElse(kv)
so I don't see any reason to use mutable collection here.
If you don't like all these Option.map
things:
m + (if (m.contains(k)) k -> f(m(k)) else kv)
Note, that there is a whole class of possible variations:
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
So, Why it's not a standard function? IMO, because all variations still can be implemented as one-liners. If you want library with all functions, that can be implemented as one-liners, you know, it's Scalaz :).
P.S. If yu also wonder why there is no "update(d) if persist" function - see @Rex Kerr 's answer here
Upvotes: 21
Reputation: 53829
You can create you own function for that purpose:
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)
Upvotes: 18