Working with immutable HashMap in Scala - scala

I have an immutable HashMap and want to add/remove values from it. The Scala api docs say that I have to use += and -= methods, but they don't work and I get the following error:
error: value += is not a member of scala.collection.immutable.HashMap
How do I add or remove values from a HashMap in Scala?

You are watching api for mutable HashMap, to add pair to immutable HashMap use +
hashMap + ("key", "value")
or if you want to remove use -
hashMap - "key"
But you should remember that it would create a new structure
As for += method, i think that this design is not good, because in such case you have to use var instead of val, and that's not a functional way

There is no method += in immutable collections, but compiler rewrites constructions like a <opname>= b to a = a <opname> b if there is no method <opname>= in a.
var myMap = Map[Int, String]()
myMap += (1, "a")
Last line actually means:
myMap = myMap + (1, "a")

It does not work because immutable map yields new instance instead of modifying existing one (thus it is immutable). So using val with immutable map is not a legal:
scala> val foo = Map.empty[Int, Int]
foo: scala.collection.immutable.Map[Int,Int] = Map()
scala> foo += 1 -> 2
<console>:9: error: value += is not a member of scala.collection.immutable.Map[Int,Int]
foo += 1 -> 2
^
scala> var bar = Map.empty[Int, Int]
bar: scala.collection.immutable.Map[Int,Int] = Map()
scala> bar += 2 -> 2
scala> bar
res2: scala.collection.immutable.Map[Int,Int] = Map(2 -> 2)
If you against using vars, opt to mutable maps.

Related

How to increment an Int value in a Map with getOrElseUpdate?

I can update a mutable.Map value with +=:
scala> val map = mutable.Map[Int,Int]()
map: scala.collection.mutable.Map[Int,Int] = Map()
scala> map(5) = 5
scala> map
res55: scala.collection.mutable.Map[Int,Int] = Map(5 -> 5)
scala> map(5) += 1
scala> map
res57: scala.collection.mutable.Map[Int,Int] = Map(5 -> 6)
But I can't use += with getOrElseUpdate and I don't understand why:
scala> map.getOrElseUpdate(5, 0) += 1
<console>:16: error: value += is not a member of Int
Expression does not convert to assignment because receiver is not assignable.
map.getOrElseUpdate(5, 0) += 1
^
I know I've used a mutable.SortedMap with mutable.ArrayBuffer values before and it let me use that type's += operation with getOrElseUpdate with no problem. Is there something like mutable.Int that I'm supposed to use?
In scala you don't get semantics of obtaining "reference" of a variable and it is because good style of scala is when you don't mutate variables on your own, so you can't do this in this way. Rather than this you can describe mutation with function rather than directly mutating variable in this way:
import scala.collection.mutable
val map = mutable.Map.empty[Int,Int]
val yourKey: Int = ???
map.updateWith(yourKey){
case Some(i) => Some(i+1)
case None => Some(1)
}
Here i used this: https://www.scala-lang.org/api/2.13.6/scala/collection/mutable/Map.html#updateWith(key:K)(remappingFunction:Option[V]=%3EOption[V]):Option[V]
Also +, += and +: functions in array buffer and other mutable collections have other semantics.

Scala - Iterate over an Iterator of type Product[K,V]

I am a newbie to Scala and I am trying to understand collectives. I have a sample Scala code in which a method is defined as follows:
override def write(records: Iterator[Product2[K, V]]): Unit = {...}
From what I understand, this function is passed an argument record which is an Iterator of type Product2[K,V]. Now what I don't understand is this Product2 a user defined class or is it a built in data structure. Moreover how do explore the key-value pair contents of Product2 and how do I iterate over them.
Chances are Product2 is a built-in class and you can easily check it if you're in modern IDE (just hover over it with ctrl pressed), or, by inspecting file header -- if there is no related imports, like some.custom.package.Product2, it's built-in.
What is Product2 and where it's defined? You can easily found out such things by utilizing Scala's ScalaDoc:
In case of build-in class you can treat it like tuple of 2 elements (in fact Tuple2 extends Product2, as you may see below), which has ._1 and ._2 accessor methods.
scala> val x: Product2[String, Int] = ("foo", 1)
// x: Product2[String,Int] = (foo,1)
scala> x._1
// res0: String = foo
scala> x._2
// res1: Int = 1
See How should I think about Scala's Product classes? for more.
Iteration is also hassle free, for example here is the map operation:
scala> val xs: Iterator[Product2[String, Int]] = List("foo" -> 1, "bar" -> 2, "baz" -> 3).iterator
xs: Iterator[Product2[String,Int]] = non-empty iterator
scala> val keys = xs.map(kv => kv._1)
keys: Iterator[String] = non-empty iterator
scala> val keys = xs.map(kv => kv._1).toList
keys: List[String] = List(foo, bar, baz)
scala> xs
res2: Iterator[Product2[String,Int]] = empty iterator
Keep in mind though, that once iterator was consumed, it transitions to empty state and can't be re-used again.
Product2 is just two values of type K and V.
use it like this:
write(List((1, "one"), (2, "two")))
the prototype can also be written like: override def write(records: Iterator[(K, V)]): Unit = {...}
To access values k of type K and v of type V.
override def write(records: Iterator[(K, V)]): Unit = {
records.map{case (k, v) => w(k, v)}
}

Updating immutable map as the side effect of getOrElse

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)

Create a map from a collection using a function

I want to create a map from a collection by providing it a mapping function. It's basically equivalent to what a normal map method does, only I want it to return a Map, not a flat collection.
I would expect it to have a signature like
def toMap[T, S](T => S): Map[T, S]
when invoked like this
val collection = List(1, 2, 3)
val map: Map[Int, String] = collection.toMap(_.toString + " seconds")
the expected value of map would be
Map(1 -> "1 seconds", 2 -> "2 seconds", 3 -> "3 seconds")
The method would be equivalent to
val collection = List(1, 2, 3)
val map: Map[Int, String] = collection.map(x => (x, x.toString + " seconds")).toMap
is there such a method in Scala?
scalaz has a fproduct method for Functors which returns things in the right shape for calling .toMap on the result:
scala> import scalaz._,Scalaz._
import scalaz._
import Scalaz._
scala> val collection = List(1, 2, 3)
collection: List[Int] = List(1, 2, 3)
scala> collection.fproduct(_.toString + " seconds").toMap
res0: scala.collection.immutable.Map[Int,String] = Map(1 -> 1 seconds, 2 -> 2 seconds, 3 -> 3 seconds)
There is no such single method. As you say, you can use map followed by toMap. If you are concerned about the intermediary list you are creating, you might consider using breakOut as the implicit second argument to map:
import scala.collection.breakOut
val map: Map[Int, String] = collection.map(x => (x._1, x._2.toString + " seconds"))(breakOut)
You can read more about breakOut and the implicit argument of map here.
This method allows you to construct other types that have a suitable CanBuildFrom implementation as well, without the intermediate step:
val arr: Array[(Int, String)] = collection.map(x => (x._1, x._2.toString + " seconds"))(breakOut)
You might also want to consider using views which inhibit creating of intermediary collections:
val m = (List("A", "B", "C").view map (x => x -> x)).toMap
The differences between these approaches are described here.
Finally, there is the mapValues method, which might be suitable for your purposes, if you are only mapping the values of each key-value pair. Be careful, though, since this method actually returns a view, and might lead to unexpected performance hits.

Adding to scala mutable map fails

I don't understand why += works when I define a tuple mapEntry beforehand and then add it to a map, while trying to add directly bypassing creating of unnecessary variable mapEntry fails. Most likely I am missing something obvious. Thanks in advance.
This works: map += mapEntry while
this fails: map += ("key-to-tom", Person("Tom", "Marx"))
Here is my complete scala REPL session
Welcome to Scala version 2.10.3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_45).
Type in expressions to have them evaluated.
Type :help for more information.
scala> case class Person(val fname:String, val lname:String)
defined class Person
scala> val map = scala.collection.mutable.Map[String,Person]()
map: scala.collection.mutable.Map[String,Person] = Map()
scala> val mapEntry = ("key-to-joe", Person("Joe", "Smith") )
mapEntry: (String, Person) = (key-to-joe,Person(Joe,Smith))
scala> map += mapEntry
res0: map.type = Map(key-to-joe -> Person(Joe,Smith))
scala> val mapEntry2 = ("key-to-ann", Person("Ann", "Kline") )
mapEntry2: (String, Person) = (key-to-ann,Person(Ann,Kline))
scala> map += mapEntry2
res1: map.type = Map(key-to-joe -> Person(Joe,Smith), key-to-ann -> Person(Ann,Kline))
scala> map += ("key-to-tom", Person("Tom", "Marx") )
<console>:11: error: type mismatch;
found : String("key-to-tom")
required: (String, Person)
map += ("key-to-tom", Person("Tom", "Marx") )
^
scala>
Your last statement fails because you need to enclose the tuple in parentheses to convey that you are adding a tuple:
map += ( ("key-to-tom", Person("Tom", "Marx")) )
The problem is that += is overloaded. Aside from a single tuple, += also can take two or more arguments. The signature is this:
def +=(elem1: (A, B), elem2: (A, B), elems: (A, B)*): Map.this.type
Your tuple is a Tuple2 (which is the same as a Pair but still just a single parameter), and this overloaded method takes 2 arguments (at least). Scala chooses the latter as the closer match.