Concatenate two Scala mutable maps preserving the keys of the first map - scala

The scala API let's you append one map to another as follows:
import scala.collection.mutable.{Map => MutableMap}
val m1: MutableMap[Int,String] = MutableMap(1 -> "A", 2 -> "B", 3 -> "C")
val m2: MutableMap[Int,String] = MutableMap(2 -> "X", 3 -> "Y", 4 -> "Z")
m1 ++= m2 // outputs: Map(2 -> X, 4 -> Z, 1 -> A, 3 -> Y)
m1 // outputs: Map(2 -> X, 4 -> Z, 1 -> A, 3 -> Y)
The behaviour is to override the repeated pairs with the pairs coming from the right map.
What is a good way to do it in the opposite way? That is, concatenating the pairs of m1 and m2 in m1 where the pairs of m1 are kept if repeated in m2.

m1 ++= (m2 ++ m1) perhaps?
Do you have to mutate m1 (that's rarely the right thing to do in scala anyway)?
You could just create a new map as m2 ++ m1 otherwise ...

Store as a list (or similar collection) and group them:
val l1 = List(1 -> "A", 2 -> "B", 3 -> "C")
val l2 = List(2 -> "X", 3 -> "Y", 4 -> "Z")
(l1 ::: l2).groupBy(_._1) //Map[Int, List[Int, String]]
//output: Map(2 -> List((2,B), (2,X)), 4 -> List((4,Z)), 1 -> List((1,A)), 3 -> List((3,C), (3,Y)))
You can of course remove the leftover integers from the Map's value lists if you want.

Related

How to replace an Item in scala Map, keeping the order

I have a Map like this .
val m1 = Map(1 -> "hello", 2 -> "hi", 3 -> "Good", 4 -> "bad")
val m2 = Map(1 -> "Great", 3 -> "Guy")
Now I need to derive another Map from m1 and m2, and I need to replace elements of m1 by those m2 with order preserved.
m3 should like
Map(1 -> "Great", 2 -> "hi", 3 -> "Guy", 4 -> "bad")
Is there a way to do it in functional programming??
Map does not preserve ordering. If you want to preserve insertion order use ListMap:
List map iterators and traversal methods visit key-value pairs in the order they were first inserted.
val m1 = ListMap(1 -> "hello", 2 -> "hi", 3 -> "Good", 4 -> "bad")
val m2 = Map(1 -> "Great", 3 -> "Guy")
val m3 = m1 ++ m2
or SortedMap if you want ordering by the key:
An immutable map whose key-value pairs are sorted according to an scala.math.Ordering on the keys.
val m3 = m1 ++ m2
However order is not preserved because Map is not ordered in the first place. If you want an ordered map use ListMap.

Filter groups by size after using groupby function in Scala

I have a map with some key-value pairs where the keys are tuples and the values are strings.
for example:
Map((1,0) -> "a", (3,1) -> "b", (1,2) -> "c")
After grouping by the first element of the tuple key:
Map((1,0) -> "a", (3,1) -> "b", (1,2) -> "c").groupBy(_._1._1)
I get the following:
(1,Map((1,0) -> a, (1,2) -> c))(3,Map((3,1) -> b))
Now my question is, what would be the proper way to filter a group with a given size? for exmaple, if I want to filter the groups with size 2 I should get the following:
(1,Map((1,0) -> a, (1,2) -> c))
Use .filter(_._2.size == 2):
scala> Map((1,0) -> "a", (3,1) -> "b", (1,2) -> "c").groupBy(_._1._1).filter(_._2.size == 2)
res0: Map[Int,Map[(Int, Int),String]] = Map(1 -> Map((1,0) -> a, (1,2) -> c))

Fixed Length SortedMap in Scala

I'm new to Scala, does Scala support a fixed length SortedMap?
What I have in mind is a map that does the following:
Takes a max_size parameter upon creation
Upon an add, checks if there are already max_size elements
If there is, remove the smallest key and its value first (key's gonna be an Int)
Then adds the key and value to the map.
Strictly speaking, I don't need the map to be sorted, but it seems necessary/available if we're removing the smallest key
I wanted to ask before I started rolling my own. Also I will be running this under Samza, which I believe is single threaded and so concurrency won't be a concern.
I'm on scala 2.10
You can do something simple like this based on TreeMap which guarantees order of elements by key:
import scala.collection.immutable.TreeMap
def add[K,V](map: TreeMap[K,V], elem: (K,V), maxSize: Int): TreeMap[K,V] = {
map.takeRight(maxSize - 1) + elem
}
Here is how to use it:
scala> val m = TreeMap(1 -> "one", 2 -> "two", 3 -> "three")
m: scala.collection.immutable.TreeMap[Int,String] =
Map(1 -> one, 2 -> two, 3 -> three)
scala> val m1 = add(m, 0 -> "zero", 4)
m1: scala.collection.immutable.TreeMap[Int,String] =
Map(0 -> zero, 1 -> one, 2 -> two, 3 -> three)
scala> val m2 = add(m1, 4 -> "four", 4)
m2: scala.collection.immutable.TreeMap[Int,String] =
Map(1 -> one, 2 -> two, 3 -> three, 4 -> four)
scala> val m3 = add(m2, 5 -> "five", 4)
m3: scala.collection.immutable.TreeMap[Int,String] =
Map(2 -> two, 3 -> three, 4 -> four, 5 -> five)
scala> val m4 = add(m3, 0 -> "zero", 4)
m4: scala.collection.immutable.TreeMap[Int,String] =
Map(0 -> zero, 3 -> three, 4 -> four, 5 -> five)
You can obviously try to make it more convenient to suit your needs.
Aleksey's answer was very helpful. I made a small fix to it
import scala.collection.immutable.TreeMap
def add[K,V](map: TreeMap[K,V], elem: (K,V), maxSize: Int): TreeMap[K,V] = {
(map + elem).takeRight(maxSize - 1)
}
val m = TreeMap(1 -> "one", 2 -> "two", 3 -> "three")
val m1 = add(m, 0 -> "zero", 4)
val m2 = add(m1, 4 -> "four", 4)
val m3 = add(m2, 0 -> "zero", 4)
val m4 = add(m3, 1 -> "one", 4)
val m5 = add(m4, 0 -> "zero", 4)
val m6 = add(m5, 1 -> "one", 4)

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...

Scala: What is the idiomatic syntax for transforming nested collections?

For example: I have a list of Maps and I'd like to create a List from the values of the 3rd "column" of the maps...
val l = List(Map(1 -> "test", 2 -> "test", 3 -> "test"), Map(4 -> "test", 5 -> "test", 6 -> "test"))
Well, there's no ordering on maps, so the "third column" part of your question doesn't really make sense. If you mean something like "return a list of values that have map key 3"), they you could do
val l = List(Map(1 -> "test1", 2 -> "test2", 3 -> "test3"), Map(1 -> "test4", 2 -> "test5", 3 -> "test6"))
val thirdArgs= for(map<-l; value <-map.get(3)) yield value
// or equivalently val thirdArgs= l.flatMap(_.get(3))
println(thirdArgs)// prints List(test3, test6)
This relies on the fact that map.get(3) returns an Option[String], and the Scala for-comprehension syntax works with Option.
If you actually meant "third column", then the data structure you want isn't a map, but a tuple.
val l = List(("test1","test2","test3"), ("test4","test5", "test6"))
val thirdArgs= for(tuple<-l) yield tuple._3
// or equivalently val thirdArgs= l.map(_._3)
println(thirdArgs)// prints List(test3, test6)
Kim, we need an almost infinite amount of "hold my hand" simple Scala solutions posted on the web, so junior programmers can google them and get a running start. Here we go:
Maybe this is what you want:
scala> val l = List(Map(1 -> "test1", 2 -> "test2", 3 -> "test3"),
| Map(1 -> "test4", 2 -> "test5", 3 -> "test6"))
>l: List[scala.collection.immutable.Map[Int,java.lang.String]] = List(Map(1 -> test1, 2 -> test2, 3 -> test3), Map(1 -> test4, 2 -> test5, 3 -> test6))
The you can get the third "row" like this:
scala> l.map( numMap => numMap(3))
res1: List[java.lang.String] = List(test3, test6)