Updating immutable map as the side effect of getOrElse - scala

Sometimes I use a Map as a memoization cache. With mutable maps, I use getOrElseUpdate:
mutableMap.getOrElseUpdate(key, {
val value = <compute the value>
value
})
Immutable maps don't have getOrElseUpdate. So I want to do this
immutableMap.getOrElse(key, {
val value = <compute the value>
immutableMap += key -> value
value
})
This seems to work in practice, I have good arguments to believe it works in theory, and it's more or less readable -- is it a terrible idea for some reason I'm missing?
The other alternatives I'm considering are
immutableMap.get(key) match {
case Some(value) => value
case None =>
val value = <compute the value>
immutableMap += key -> value
value
}
which is not much different and is more cumbersome, or
if (immutableMap.contains(key)) {
immutableMap(key)
} else {
val value = <compute the value>
immutableMap += key -> value
value
}
which is the dumbest and probably least idiomatic.
In principle I rather not go for a solution that uses a helper to return the value and the updated map, unless it's the unarguably superior way.

Sure, it seems reasonable except for one small issue... it's not updating your collection! If you're using an immutable Map, then that Map is immutable. You can not change it, ever.
In fact, immutable Map from Scala collection does not even have a += method defined on it, see immutable.Map. All the methods with "append" or "add" new values to the Map actually return a new Map. So for what you've written above to compile, you'd have to not be using something immutable.
To do this with an immutable map, you'll need to work with a var and replace that var with the new Map (which can lead to issues with threading) or you have to adopt a State Monad type pattern in which you return not only the new value but also the new Map.
def getOrCalc(m: Map[Key, Value], k: Key)(f: Key => Value): (Map[Key, Value], Value] ={
if(m.contains(k)) (m, m(k))
else{
val value = f(k)
(m +: (k, value), value)
}
}

My only recommendation (regarding the reasons why you choosed var instead of mutable.Map or Java's ConcurrentMap) is to wrap it into DSL, like:
case class Mutable[K,V](var m: Map[K,V]) {
def orElseUpdate(key: K, compute: => V) = m.getOrElse(key, {
val value = compute
m += key -> value
value
})
}
scala> val a = Mutable(Map(1 -> 2))
a: Mutable[Int,Int] = Mutable(Map(1 -> 2))
scala> a.orElseUpdate(2, 4)
res10: Int = 4
scala> a.orElseUpdate(2, 6)
res11: Int = 4
scala> a.orElseUpdate(3, 6)
res12: Int = 6
Another option (if your computation is lightweight) is just:
m += key -> m.getOrElse(key, compute)
m(key)
Example:
scala> var m = Map(1 -> 2)
m: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2)
scala> m += 3 -> m.getOrElse(3, 5)
scala> m
res1: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 5)
scala> m += 3 -> m.getOrElse(3, 5)
scala> m
res3: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 5)
scala> m += 3 -> m.getOrElse(3, 6)
scala> m
res5: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 5)
You can wrap it into DSL as well:
implicit class RichMap[K,V](m: Map[K,V]) {
def kvOrElse(k: K, v: V) = k -> m.getOrElse(k, v)
}
scala> m += m.kvOrElse(3, 7)
scala> m
res7: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 5)
scala> m += m.kvOrElse(4, 7)
scala> m
res9: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 5, 4 -> 7)

Related

How to calculate running total for a SortedMap / TreeMap

So I have the following SortedMap:
val mySortedMap: SortedMap[Double, Int] = SortedMap(1.1 -> 7, 2.4 -> 3, 6.5 -> 12)
Now I need to calculate the running total for each key, so the output should look like this:
val result: SortedMap[Double, Int] = SortedMap(1.1 -> 7, 2.4 -> 10, 6.5 -> 22)
I know I can do something similar by using scanLeft:
val result: Iterable[Int] = mySortedMap.scanLeft(0)((c, e) => c + e._2)
But this returns Iterable whereas I need to keep my SortedMap as specified above. What is the most functional / efficient way to do so?
You can use foldLeft where your accumulator is both, a new SortedMap and the running total.
import scala.collection.immutable.SortedMap
def runningTotal(map: SortedMap[Double, Int]): SortedMap[Double, Int] = {
val (transformed, _) = map.foldLeft((SortedMap.empty[Double, Int], 0)) {
(acc, element) =>
val (mapAcc, totalAcc) = acc
val (key, value) = element
val newTotal = totalAcc + value
val newMap = mapAcc + (key -> newTotal)
(newMap, newTotal)
}
transformed
}
Bonus, here is your solution but using Iterators instead, thus it would be a little bit more efficient.
def runningTotal(map: SortedMap[Double, Int]): SortedMap[Double, Int] = {
val newValues = map.valuesIterator.scanLeft(0) {
(acc, value) => acc + value
}.drop(1)
map.keysIterator.zip(newValues).to(SortedMap)
}
Ok, I think I might have found one possible solution:
val input: SortedMap[Double, Int] = SortedMap(1.1 -> 7, 2.4 -> 3, 6.5 -> 12)
val aggregates: Iterable[Int] = mySortedMap.scanLeft(0)((c, e) => c + e._2).tail
val sortedAggregates: SortedMap[Double, Int] = input.keySet.zip(aggregates).to(SortedMap)
println(sortedAggregates)
gives TreeMap(1.1 -> 7, 2.4 -> 10, 6.5 -> 22).
I wonder if there is a better way?

Is there a native grouping function that works like the sortWith function?

There are a few libraries such as Spark and other Scala extensions that have the "groupWith" function available. This function allows you to compare an element to the rest of the collection and then group it using one or more predicates. There doesn't seem to be any native functionality in Scala for this but they do have the sortWith function that behaves similarly but only sorts the items instead of grouping them. If the explanation isn't sufficient here's a small code sample that should display what I'm trying to do:
val list = List(1,2,3,4,5,5)
val groupedList = list.groupWith{ (e,c) =>
e == c
}
This is a very simple example and I want to do more complicated comparisons such as
e + 1 == c
So again the question is are there any native Scala functions that do this? Any suggestions or workarounds?
Update:
From the simple examples given it seems it's not exactly clear what I'm trying to do, here's a better example:
Say I have a case class and a list of these objects:
case class Item(num: Int, color: String)
val list = List(new Item(13, "red"), new Item(14,"red"), new Item(15, "blue"), new Item(16, "red"))
list.groupWith{ (e,c) =>
(e.num -1 == c.num || e.num + 1 == c.num ) && e.color == c.color
}
And this should return something like this:
res8: List[List[Item]] = List(List(Item(13,red), Item(14,red)), List(Item(15,blue)), List(Item(16,red)))
Here's an implementation:
// Takes the list as a parameter, can use pimp-my-library if you want
def groupWith[A](xs: List[A], f: (A, A) => Boolean) = {
// helper function to add "e" to any list with a member that matches the predicate
// otherwise add it to a list of its own
def addtoGroup(gs: List[List[A]], e: A): List[List[A]] = {
val (before, after) = gs.span(_.exists(!f(_, e)))
if (after.isEmpty)
List(e) :: gs
else
before ::: (e :: after.head) :: after.tail
}
// now a simple foldLeft adding each element to the appropriate list
xs.foldLeft(Nil: List[List[A]])(addtoGroup)
}
groupWith(list, { (e: Item, c: Item) =>
(e.num - 1 == c.num || e.num + 1 == c.num) && e.color == c.color})
//| res0: List[List[groups.groups.Item]] =
// List(List(Item(16,red)),
// List(Item(15 ,blue)),
// List(Item(14,red), Item(13,red)))
Not sure if this what you want (check my comments to your question), but there is method groupBy defined in GenTraversableLike which List inherits (not only List). You will get:
scala> val list = List(1,2,3,4,5,5)
list: List[Int] = List(1, 2, 3, 4, 5, 5)
scala> list.groupBy( el => el )
res0: scala.collection.immutable.Map[Int,List[Int]] = Map(5 -> List(5, 5), 1 -> List(1), 2 -> List(2), 3 -> List(3), 4 -> List(4))
scala> list.groupBy( el => el + 1 )
res1: scala.collection.immutable.Map[Int,List[Int]] = Map(5 -> List(4), 6 -> List(5, 5), 2 -> List(1), 3 -> List(2), 4 -> List(3))
Basically you need to provide discriminator function from value to key and you will get Map[Key, List[Value].
Is this what you want?

How to find key index in a map?

I have a Map and add index in map by using
val indexByColName = sorted.view.zipWithIndex.toMap
How do I find index for a specific key?
Method indexWhere for instance in Array provides this semantics as follows,
implicit class keyIndexing[A,B](val m: Map[A,B]) extends AnyVal {
def keyIndex(key: A, value: B) = {
m.toArray.indexWhere{ case (k,v) => key == k && value == v }
}
}
Then for
val sorted = Map("a" -> 10, "b" -> 11, "c" -> 12)
scala> sorted.keyIndex("a",10)
res15: Int = 0
and for a non matching (or non existing) key and value,
scala> sorted.keyIndex("a",11)
res16: Int = -1
scala> sorted.keyIndex("z",11)
res19: Int = -1
Defining sorted and showing the output you want would help but I think this might do it.
scala> val sorted = Map("zero" -> 0, "one" -> 1, "two" -> 2)
sorted: scala.collection.immutable.Map[String,Int] = Map(zero -> 0, one -> 1, two -> 2)
scala> val indexByColName =sorted.view.zipWithIndex.toMap
indexByColName: scala.collection.immutable.Map[(String, Int),Int] = Map((zero,0) -> 0, (one,1) -> 1, (two,2) -> 2)
scala> indexByColName.get(("zero", 0)).get
res1: Int = 0
scala> indexByColName.get(("two", 2)).get
res3: Int = 2

Get value with the lowest key value from Map[Int, String]

Say I have a map: Map[Int, String]. How would I get the value [String] with the lowest key [Int]. I've been trying to implement this functionally, but just can't figure out how to do this.
The following code will get you a value with a lowest key (ignoring some corner cases).
def lowestKeyMember[A](m: Map[Int,A]): A = m(m.keys.min)
This will break ties arbitrarily and throw on an empty map. If you need to do this operation frequently and/or on large maps, you should look into SortedMap.
Maps are not normally sorted. You could however use a SortedMap, then the map will be sorted and the first value will be the head. All you need to do is retrieve the head.
map.head()
Come on, people! "Functionally" is code word for "folding".
scala> val m = Map(1->"eins",2->"zwei",3->"drei")
m: scala.collection.immutable.Map[Int,String] = Map(1 -> eins, 2 -> zwei, 3 -> drei)
scala> m.foldLeft(Int.MaxValue -> "") { case (min,p) => if (min._1 <= p._1) min else p }
res0: (Int, String) = (1,eins)
But an 8-char operator?
Let's see, is that enough parens? Don't tell me -> is like - and /: is like /.
scala> (Int.MaxValue -> "" /: m) { case (min,p) => if (min._1 <= p._1) min else p }
<console>:9: error: missing arguments for method /: in trait TraversableOnce;
follow this method with `_' if you want to treat it as a partially applied function
(Int.MaxValue -> "" /: m) { case (min,p) => if (min._1 <= p._1) min else p }
^
Oh, well, OK.
scala> ((Int.MaxValue -> "") /: m) { case (min,p) => if (min._1 <= p._1) min else p }
res2: (Int, String) = (1,eins)
Or,
scala> import math.Ordering.Implicits._
import math.Ordering.Implicits._
scala> ((Int.MaxValue -> "") /: m) { case (min,p) if min <= p => min case (_, p) => p }
res5: (Int, String) = (1,eins)
A variant of the _.keys.min solution that works with Options (i.e. will not throw on an empty map):
scala> val a : Map[Int, String]=Map(1 -> "1", 2 -> "2")
a: Map[Int,String] = Map(1 -> 1, 2 -> 2)
scala> val b : Map[Int, String]=Map()
b: Map[Int,String] = Map()
scala> def valueForMinKey[K,V](a : Map[K,V])(implicit cmp : Ordering[K]) = a.keys.reduceOption(cmp.min(_, _)).map(a(_))
valueForMinKey: [K, V](a: Map[K,V])(implicit cmp: Ordering[K])Option[V]
scala> valueForMinKey(a)
res27: Option[String] = Some(1)
scala> valueForMinKey(b)
res28: Option[String] = None
In this example, the implicit parameter cmp will be satisfied by Ordering.Int. The example will work with any Map where the keys can be ordered (and a matching implict can be found by the compiler).

How to modify a value of a Map which contains Sets, returning a new Map?

Given a Map[Int, Set[Int]], how can I modify a single value of the Map, generating a new one in the process, for example:
val x = Map(1 -> Set(1,2,3))
x(1) + 5 // This creates a new Set, but not a new Map
val y = x(1) change { x => x + 5 }
// The previous functionality is what I'm looking for
// z: Set[Int]] = List(Set(1, 2, 3, 5))
As Robin Green suggests, lenses are made for this job. In fact, you want a partial lens, since a map is a partial function of key -> value.
Scalaz 7 includes the mapVPLens function to make a partial lens (PLens) to the value at a chosen key:
import scalaz.PLens._
val x = Map(1 -> Set(1,2,3))
mapVPLens(1) mod ((_: Set[Int]) + 5, x) // Map(1 -> Set(1, 2, 3, 5))
Modifying the value at a non-existent key will have no effect:
mapVPLens(9) mod ((_: Set[Int]) + 5, x) // Map(1 -> Set(1,2,3))
In scala 2.10:
implicit class ChangeableMap[K,V]( val m: Map[K,V] ) extends AnyVal {
def change( k: K )( transform: V => V ): Map[K,V] = {
m.get( k ).map{ v => m + (k-> transform(v)) }.getOrElse( m )
}
}
Some test:
scala>val x = Map(1 -> Set(1,2,3), 2 -> Set(4,5))
x: scala.collection.immutable.Map[Int,scala.collection.immutable.Set[Int]] = Map(1 -> Set(1, 2, 3), 2 -> Set(4, 5))
scala> x.change(1) { x => x + 5 }
res1: Map[Int,scala.collection.immutable.Set[Int]] = Map(1 -> Set(1, 2, 3, 5), 2 -> Set(4, 5))
If you're in scala 2.9, this will do:
class ChangeableMap[K,V]( m: Map[K,V] ) {
def change( k: K )( transform: V => V ): Map[K,V] = {
m.get( k ).map{ v => m + (k-> transform(v)) }.getOrElse( m )
}
}
implicit def toChangeableMap[K,V]( m: Map[K,V] ) = new ChangeableMap[K,V]( m )
Use lenses!
However, Scalaz 6, which defines lenses, doesn't have a specific pre-made lens for your situation, which means slightly more work for you - though if your Map is in turn contained in another object, it does have (well-hidden) support for that situation. And Scalaz 7 will have a lens for standalone Maps.
Also, lenses are just pairs of functions, requiring no language support, so you could just roll your own.
Here's one from our codebase.
/**
* Alters a value in a map.
*
* modifyMap :: Map k v -> k -> (Maybe v -> Maybe v) -> Map k v
* See Haskell's Data.Map.alter
*
* #param m the map to modify
* #param key the key to modify the value of
* #param mod a function that takes the existing value (if any) and returns an optional new value
*
* #return the modified map
*/
def modifyMap[K,V](m: Map[K,V], key: K)
(mod: (Option[V] ⇒ Option[V])): Map[K,V] = {
mod(m.get(key)) match {
case Some(newVal) ⇒ m + (key → newVal)
case None ⇒ m - key
}
}
And here's how you use it:
modifyMap(myMap, "someKey") {
case Some(someVal) =>
// present
if (condition)
Some(valueDerivedFrom(someVal)) // provide a new mapping for someKey
else
None // someKey will now be unset
case None =>
// wasn't present
if (condition)
Some(newValue) // provide a new value for someKey
else
None // leave someKey unset
}
A very idiomatic way of solving this problem would be the following (thanks Viktor Klang):
val x = Map(1 -> Set(1,2,3), 2 -> Set(1), 3 -> Set(5))
x.map { case (1, v) => (1, v + 5); case x => x }
// res0: Map(1 -> Set(1, 2, 3, 5))
Or nicely packed into a class as well as an implicit:
class ChangeableMap[K,V](map:Map[K,V]) {
def change(index:K)(f:V => V) = map.map {
case (`index`, v:V) => (index, f(v))
case x => x
}
}
object ChangeableMap {
implicit def fromMap[K,V](map:Map[K,V]) = new ChangeableMap(map)
}
With the previous declaration, the following will work:
x.change(1) { x => x + 5 }
x.change(1) { _ + 5 }
// res1: Map(1 -> Set(1, 2, 3, 5))
Note that this is probably not the fastest solution, given that Scala will (probably, haven't confirmed) iterate over the entire map!
A possibly faster implementation would be the following (though I have not verified if it is actually faster):
class ChangeableMap[K,V](map:Map[K,V]) {
def change(index:K)(f:V => V) = map.get(index) match {
case Some(x) => map + ((index, f(x)))
case None => map
}
}
I think the easiest way would be using scala.collection.mutable.Map.
import scala.collection.mutable.Map
val m = Map(1 -> Set(1,2,3))
m.update(1, m(1) + 5)
// now the Map looks like this: Map(1 -> Set(1,2,3,5))
If you get an immutable Map, you can simply convert it to a mutable one by using:
val n: collection.mutale.Map(m.toSeq: _*)
This also works the other way around, if you need to return an immutable Map.
As mentioned before you can use Partial Lens for this sort of problem, scalaz and Monocle implements it. Here is how you would do it with Monocle:
import monocle.syntax.taversal._ // to use |->>
import monocle.syntax.at._ // to use at
val x = Map(1 -> Set(1,2,3))
x |->> at(1) modify(_ + 5) == Map(1 -> Set(1,2,3,5))