Different Representations of Scala HashMap - scala

I've been playing around with the Scala HashMap and I've noticed two different representations of the HashMap. I was wondering if somebody could explain the difference of:
Map(123 -> 1)
and
{123=1}
Thanks!

Where have you seen {123=1}? It's not a standard representation in Scala, but it is the way Java defines toString for its Maps.
val sm = Map(1->1, 2->2) // Map(1 -> 1, 2 -> 2)
val jm = new java.util.HashMap[Int,Int]()
jm.put(1,1)
jm.put(2,2)
jm
// java.util.HashMap[Int,Int] = {1=1, 2=2}

-> is a method that creates tuples. By itself it doesn't directly have anything to do with maps. So for example 123 -> 1 returns a tuple (123, 1). You can try this in the REPL:
scala> 123 -> 1
res1: (Int, Int) = (123,1)
You can create a map by supplying tuples to object Map's apply method, which is what you are doing when you do this:
val m = Map(123 -> 1, 456 -> 2)
is the same as
val m = Map.apply(123 -> 1, 456 -> 2)
is the same as
val m = Map.apply((123, 1), (456, 2))
which creates a Map with two entries, one with key 123 and value 1, the other one with key 456 and value 2.

Related

Scala - reducing a map based on changed key

Lets say have the following Map data
val testMap: Map[String, Int] = Map("AAA_abc" -> 1,
"AAA_anghesh" -> 2,
"BBB_wfejw" -> 3,
"BBB_qgqwe" -> 4,
"C_fkee" -> 5)
Now I want to reduce the map by key.split("_").head and add all the values for the keys that became equal. So for this example the Map should result into:
Map(AAA -> 3, BBB -> 7, C -> 5)
What would be the correct way to do so in Scala?
I tried constructions with groupBy and reduceLeft but could not find a solution.
Here's a way to do it:
testMap.groupBy(_._1.split("_").head).mapValues(_.values.sum)
A variation in one pass:
testMap.foldLeft(Map[String,Int]())( (map, kv) => {
val key = kv._1.split("_").head
val previous = map.getOrElse(key,0)
map.updated(key, previous + kv._2) })

How to add all the values of a map without using recurrsion or var

I want to add all the values in a map without using var or any mutable structures. I have tried to do something like this but it doens't work:
val mymap = ("a" -> 1, "b" -> 2)
val sum_of_alcohol_consumption =
for ((k,v) <- mymap ) yield (sum_of_alcohol_consumption += v)
I have been told that I can use .sum on a list
Please help
Thanks
You can use the .values function of a Map to return an Iterable List of its values (all of the Integers) and then call the .sum function on that:
val myMap = Map("a" -> 1, "b" -> 2)
val sum = myMap.values.sum
println(sum) // Outputs: 3
An equivalent answer to the more elegant use of sum is to use a fold operation. sum is implemented in a manner similar to this:
val myMap = Map("a" -> 1, "b" -> 2)
val sumAlcoholConsumption = myMap.values.foldLeft(0)(_ + _)
values returns a sequence of only the values in the map. The first foldLeft argument is the zero value (think of it as the initial value for an accumulator value) for the operation. The second argument is a function that adds the current value of the accumulator to the current element, returning the sum of the two values - and it is applied to each value in turn. That said, sum is a lot more convenient.
To get the only values of map, it provides a function values which will return iterable,we can directly appy sum function to it.
scala> val mymap = Map("a" -> 1, "b" -> 2)
mymap: scala.collection.immutable.Map[String,Int] = Map(a -> 1, b -> 2)
scala> mymap.values.sum
res7: Int = 3

Custom ordering in TreeMap

Here are examples that I have been playing with:
import collection.immutable.{TreeSet, TreeMap}
val ts = TreeSet(9, 23, 1, 2)
ts
val tm = TreeMap(3 -> "c", 1 -> "a", 2 -> "b")
tm
// convert a map to a sorted map
val m = Map("98" -> List(4, 12, 14), "001" -> List(22, 11))
val t = TreeMap(m.toSeq: _*)
t // sorted by key
// sort an unsorted map
m.toSeq.sortWith((x, y) => x._2(0) < y._2(0))
// add a unsorted map into a sorted map
val m1 = Map("07" -> List(3, 5, 1), "05" -> List(12, 5, 3))
val t1: TreeMap[String, List[Int]] = t ++ m1
t1 // "001" is the first key
I can use sortWith on a Map to get a custom ordering, what if I want to use a TreeMap that uses a different ordering than the default?
You can't use Map's values to define default ordering of a Map.
TreeMap[A,B]'s constructor accepts an implicit Ordering[A] parameter, so you could do something like this:
// Will sort according to default Int ordering (ascending by numeric value)
scala> val tm = TreeMap(3 -> "c", 1 -> "a", 2 -> "b")
tm: scala.collection.immutable.TreeMap[Int,String] = Map(1 -> a, 2 -> b, 3 -> c)
// A wild implicit appears! (orders descending by numeric value)
scala> implicit val tmOrd = Ordering[Int].on((x:Int) => -x)
tmOrd: scala.math.Ordering[Int] = scala.math.Ordering$$anon$5#1d8e2eea
// Our implicit is implicitly (yeah) used by constructor
scala> val invTm = TreeMap(3 -> "c", 1 -> "a", 2 -> "b")
invTm: scala.collection.immutable.TreeMap[Int,String] = Map(3 -> c, 2 -> b, 1 -> a)
Note that it's safer to limit a scope of implicits like this one. If you can, you should construct an (not-implicit) object and pass it manually, or separate the scope of implicit declaration from the place where other code can be affected by its presence.
The reason behind this is that TreeMap is built on top of a tree that uses keys' values to maintain structure constraints that allow for efficient data reads/writes based on keys, which is the primary purpose of a Map. Ordering on values in a Map simply makes no sense.
Upd.: The complexity of ordering logic doesn't mean anything. According to your comment:
scala> object ComplexOrdering extends Ordering[Int] {
| def compare(a: Int, b: Int) = {
| if(a == 3) -1 else if(a == 2 * b) -1 else if(a == 3 * b) 0 else 1
| }
| }
defined object ComplexOrdering
scala> val tm = TreeMap(3 -> "c", 1 -> "a", 2 -> "b")
tm: scala.collection.immutable.TreeMap[Int,String] = Map(1 -> a, 2 -> b, 3 -> c)
scala> val tm = TreeMap(3 -> "c", 1 -> "a", 2 -> "b")(ComplexOrdering)
tm: scala.collection.immutable.TreeMap[Int,String] = Map(3 -> c, 2 -> b, 1 -> a)
TreeMap is defined as a Map-like type with a specified ordering of its keys. That ordering is given by an implicit parameter to the constructor:
new TreeMap()(implicit ordering: Ordering[A]) // For TreeMap[A,B]
so you can set an alternative ordering on the keys at construction by explicitly providing a custom Ordering[A].
The class does not, however, provide any (direct) means of setting an ordering based on the values. What you have with calling .toSeq.sortWith is about the best you can do as far as I know, short of coding your own collection type.

Compare two Maps in Scala

Is there any pre-defined function that I can use to compare two Maps based on the key and give me the difference? Right now, I iterate Map1 and foreach key, I check if there is an element in Map2 and I pattern match to find the difference. Is there a much elegant way to do this?
Consider the difference between the maps converted into sets of tuples,
(m1.toSet diff m2.toSet).toMap
Try:
val diff = (m1.keySet -- m2.keySet) ++ (m2.keySet -- m1.keySet)
diff contains the elements that are in m1 and not in m2 and that are in m2 and not in m1.
This solution looks like right way:
scala> val x = Map(1 -> "a", 2 -> "b", 3 -> "c")
x: scala.collection.immutable.Map[Int,String] = Map(1 -> a, 2 -> b, 3 -> c)
scala> val y = Map(1 -> "a", 2 -> "b", 4 -> "d")
y: scala.collection.immutable.Map[Int,String] = Map(1 -> a, 2 -> b, 4 -> d)
scala> val diff : Map[Int, String] = x -- y.keySet
diff: Map[Int,String] = Map(3 -> c)
Found it here https://gist.github.com/frgomes/69068062e7849dfe9d5a53bd3543fb81
I think the -- operator will do what you're looking for: http://www.scala-lang.org/api/current/index.html#scala.collection.Map#--(xs:scala.collection.GenTraversableOnce[A]):Repr
Although this will probably only work given the assumption that Map2 is always a subset of Map1...

Select first 'N' elements from map in Scala

Is there an elegant method of extracting first 'N' elements from a Map ?
I could create a new Map and iterate over the values that are to be selected, is there a function that accomplishes this ?
From the docs for the take method on Map:
Selects first n elements.
Note: might return different results for different runs, unless the
underlying collection type is ordered.
In the case of maps the collection isn't ordered, so don't count on getting the first n elements—in fact the concept of the first n elements doesn't even exist for maps.
But take will give you some first n elements, and it sounds like this is what you want:
scala> Map('a -> 1, 'b -> 2, 'c -> 3).take(2)
res1: scala.collection.immutable.Map[Symbol,Int] = Map('a -> 1, 'b -> 2)
In this case you happen to get the two elements that came first in the definition, but don't count on this happening.
Sounds like you're looking for a SortedMap, along with take(n) as discussed by others.
scala> val map = Map[String,Int]("one"->1,"two"->2,"three"->3)
map: scala.collection.immutable.Map[String,Int] =
Map(one -> 1, two -> 2, three -> 3)
scala> val n = 2
n: Int = 2
scala> val firstN = map.take(n)
firstN: scala.collection.immutable.Map[String,Int] = Map(one -> 1, two -> 2)