Iterating over list of maps and transforming - scala

I am pretty much new to Scala and trying to learn some stuff and would like to understand the best possible approach on below:
I have a list of maps as below.
List<Map<String,String>> listOfMaps = new ArrayList<>();
Map<String,String> map1 = new HashMap<>();
map1.put("a","A1");
map1.put("b","B1");
.
.
.
map10.put("a","A10");
map10.put("b","B10");
listOfMaps.add(map1);
listOfMaps.add(map2);
...
listOfMaps.add(map10);
I would like to iterate this and transform in to another list with just a certain key, value pair from the map and add a new value
List<Map<String,String>> newlistOfMaps = new ArrayList<>();
for(Map map: listOfMaps) {
Map<String,String> newMap = new HashMap<>();
newMap.put("a_b", map.get("a")+"_"+map.get("b"));
newListOfMaps.add(newMap);
}
System.out.println(newListOfMaps);
What is the best-practice to follow while doing this in Scala? Any help or suggestion would be great!

Does this look like what you're seeking?
Pay close attention to the syntax - you will notice it's far easier to construct a List and Map in Scala than in Java.
scala> val listOfMaps: List[Map[String, String]] = List(
Map("a" -> "A1", "b" -> "B1"),
Map("a" -> "A2", "b" -> "B2"),
Map("a" -> "A3", "b" -> "B3")
)
listOfMaps: List[Map[String,String]] = List(Map(a -> A1, b -> B1), Map(a -> A2, b -> B2), Map(a -> A3, b -> B3))
scala> val mapsCombined: List[Map[String, String]] = listOfMaps.map{ m =>
Map("a_b" -> s"${m.apply("a")}_${m.apply("b")}") // string interpolation to avoid String concatenation
}
mapsCombined: List[Map[String,String]] = List(Map(a_b -> A1_B1), Map(a_b -> A2_B2), Map(a_b -> A3_B3))
For mapsCombined we can do
listOfMaps.map{ m =>
Map("a_b" -> s"${m("a")}_${m("b")}")
}
// no .apply as calling with the paren directly is it's equivalent in Scala, also the same as .get on Java.

Use Scala's map function to transform the list elements (i.e. Maps) into their corresponding concatenated keys-values:
scala> val mapList: List[Map[String, String]] = List(
Map("a" -> "A1", "b" -> "B1"), Map("a" -> "A2", "b" -> "B2"), Map("a" -> "A3", "b" -> "B3")
)
mapList: List[Map[String,String]] = List(Map(a -> A1, b -> B1), Map(a -> A2, b -> B2), Map(a -> A3, b -> B3))
scala> val newMapList: List[Map[String, String]] = mapList.map( m =>
Map(m.keys.mkString("_") -> m.values.mkString("_"))
)
newMapList: List[Map[String,String]] = List(Map(a_b -> A1_B1), Map(a_b -> A2_B2), Map(a_b -> A3_B3))
Note that the same function will work if you decide to expand the original Maps to, say:
Map("a" -> "A1", "b" -> "B1", c -> "C1", ...),
Map("a" -> "A2", "b" -> "B2", c -> "C2", ...),
...

Related

In scala how to combine List of Map, of unequal size based on common keys with same value

Input:
val list1 = List(
Map("ID" -> "123", "NAME" -> "P1", "PID" -> "321"),
Map("ID" -> "456", "NAME" -> "P2", "PID" -> "333")
)
val list2 = List(
Map("ID" -> "123", "ADDRESS" -> "A1")
)
val list3 = List(
Map("PID" -> "321", "PHONE" -> "2222"),
Map("PID" -> "789", "PHONE" -> "4444")
)
Output:
List(
Map(
"ID" -> "123",
"NAME" -> "P1",
"PID" -> "321",
"ADDRESS" -> "A1",
"PHONE" -> "2222"
)
)
I tried iterating list over flatmate and map, but it had bad time complexity.
Expecting a solution in more functional programming approach, not using loop.
Thanks in advance for helping with providing solution.
list1
.flatMap { l1 =>
list2
.flatMap { l2 =>
list3
.map { l3 =>
if ((l1.ID === l2.ID) && (l1.PID === l3.PID)) {
val data = Map(
"ID" -> l1.ID,
"NAME" -> l1.NAME,
"PID" -> l1.PID,
"ADDRESS" -> l2.ADDRESS,
"PHONE" -> l3.PHONE,
)
val temp = List(data)
temp
}
}
}
}
(list1 ++ list2 ++ list3).reverse.reduce(_ ++ _)
This creates a single list of Maps and then reduces them to a single Map. The reverse is needed so that the earlier values take precedence.
I would first create two Maps, one for ID -> ADDRESS and one for PID -> PHONE from list2 and list3 respectively.
scala> val addressByID = list2.map(x => x("ID") -> x("ADDRESS")).toMap
val addressByID: Map[String, String] = Map(123 -> A1)
scala> val phoneByPID = list3.map(x => x("PID") -> x("PHONE")).toMap
val phoneByPID: Map[String, String] = Map(321 -> 2222, 789 -> 4444)
Then use for to assemble the data:
scala> for {
x <- list1
address <- addressByID.get(x("ID"))
phone <- phoneByPID.get(x("PID"))
} yield x ++ Map("ADDRESS" -> address, "PHONE" -> phone)
val res1: List[Map[String, String]] = List(HashMap(NAME -> P1, PID -> 321, PHONE -> 2222, ADDRESS -> A1, ID -> 123))

How to transform two Maps into a single Map

Suppose I have two Maps like these
Map(("a" -> "x-100"), ("b" -> "x-200"), ("c" -> "x-300"))
Map(("a" -> "y-100"), ("b" -> "y-200"), ("c" -> "y-300"))
What would be the simplest way to transform them into the following (assuming that all the values are unique and with possibly different lengths)?
Map(("x-100" -> "y-100"), ("x-200" -> "y-200"), ("x-300" -> "y-300"))
Or with a for comprehension and some safety:
for{
(k, v1) <- m1
v2 = m2.get(k)
if (v2.isDefined)
} yield (v1 -> v2.get)
This returns Map(x-100 -> y-100, x-200 -> y-200, x-300 -> y-300)
Check ScalaFiddle
val m1 = Map(("a" -> "x-100"), ("b" -> "x-200"), ("c" -> "x-300"),("d" -> "ignored"))
val m2 = Map(("a" -> "y-100"), ("b" -> "y-200"), ("c" -> "y-300"))
m1.keySet.intersect(m2.keySet).map(k=>m1(k)->m2(k)).toMap
You can do it like this:
val a = Map(("a" -> "x-100"), ("b" -> "x-200"), ("c" -> "x-300"))
val b = Map(("a" -> "y-100"), ("b" -> "y-200"), ("c" -> "y-300"))
def zipMaps(map1: Map[String, String], map2: Map[String, String]) = {
for(key <- map1.keys ++ map2.keys)
yield (key, map1.get(key), map2.get(key))
}
val result = zipMaps(a,b).map{
case (k,Some(v1),Some(v2)) => (v1,v2)
case (k,_ ,_)=> ("", "")
}.toMap.filterKeys(_ != "")
// result = Map(x-100 -> y-100, x-200 -> y-200, x-300 -> y-300)
val a = Map("a" -> "x-100", "b" -> "x-200", "c" -> "x-300")
val b = Map("a" -> "y-100", "b" -> "y-200", "c" -> "y-300")
val c = a.map {
case (k, v) => v -> b(k)
}
println(c) // Map(x-100 -> y-100, x-200 -> y-200, x-300 -> y-300)

Convert List[Map[String,Map[String,Int]]] to Map[Int,Int] in Scala

Given the following:
val t: List[Map[String, Map[String, Int]]] = List(
Map("a" -> Map("m" -> 1, "l" -> 21)),
Map("a" -> Map("m" -> 2, "l" -> 22)),
Map("a" -> Map("m" -> 3, "l" -> 23)),
Map("a" -> Map("m" -> 4, "l" -> 24))
)
I want the result:
Map(1->21,2->22,3->23,4->24)
What I have so far is:
val tt = (for {
(k,v) <- t
newKey = v("m")
newVal = v("l")
} yield Map(newKey -> newVal)).flatten.toMap
But this does not type check so Im missing some basic understanding since I cant understand why not?
My questions are:
Why is my code faulty?
What would be the most idiomatic way to do the transformation I want?
You've got List[Map[...]], not Map[...] so you want to unpack that first.
val tt = (for {
map <- t
(k, v) <- map
} ...)
t
.iterator
.flatMap(_.values)
.map { v => v("m") -> v("l") }
.toMap

Merge map and list to get a tuple3?

I have a Map[String,String] and I have a List[String].
I want to merge both of them to make a Tuple[String , String , String].
What is the most efficient way to achieve this? I tried doing this but it doesnt work :
val queryTimeMap = logToMap(reqSlowQueryData)
val iter = qNumber.iterator
val tup : Tuple3[String , String , String]= queryTimeMap.map(element=> {
(element._1, element._2 , iter.next())
})
Using a for comprehension given
Map( "a"->"aa", "b"->"bb" )
m: Map(a -> aa, b -> bb)
val l = List( "x", "y" )
l: List(x, y)
like this,
for ( ((k,v),i) <- m zip l ) yield (k,v,i)
res: List((a,aa,x), (b,bb,y))
You could do something like the below which uses a view to avoid creating an intermediate collection (via zip):
val map: Map[String, String] = Map("abc"-> "ABC", "def" -> "DEF", "ghi" -> "GHI")
val list: List[String] = List("One", "Two", "Three")
val combined: List[Tuple3[String, String, String]] = map.view.zip(list).map(x => (x._1._1, x._1._2, x._2)).toList

Scala: Remove none elements from map and flatten

I have a map:
Map("key1" -> Some("value1"), "key2" -> None, "key3" -> Some("value3"))
I want to remove all None elements and flatten the map. What is the easiest way to accomplish that? I only found this way:
Map("key1" -> Some("value1"), "key2" -> None, "key3" -> Some("value3")).filter(_._2.nonEmpty).map(item => (item._1 -> item._2.getOrElse(Nil)))
The result is:
Map(key1 -> value1, key3 -> value3)
Do you know a better way?
My take using pattern matching is:
Map("key1" -> Some("value1"), "key2" -> None, "key3" -> Some("value3")).collect {
case (key, Some(value)) => key -> value
}
// Map(key1 -> value1, key3 -> value3)
Collect acts like combined map + filter
You can use for-comprehension + pattern-matching:
for((k, Some(v)) <- yourMap) yield k -> v
Using partition over the map, like this,
val (flattened,_) = map.partition(_._2.isDefined)
My take using for comprehensions:
val m = Map("key1" -> Some("value1"), "key2" -> None, "key3" -> Some("value3"))
for( (key,value) <- m if(value.isDefined)) yield (key,value.get)
You could define the following helpers too, which allow for more compact syntax
implicit class RichPairedOptionIterableOps[A, B, Repr[_]](
iterable: IterableOps[(A, Option[B]), Repr, Repr[(A, Option[B])]]
) {
def collectWithSome: Repr[(A, B)] = iterable.collect { case (a, Some(b)) => a -> b }
def collectWithNone: Repr[A] = iterable.collect { case (a, None) => a }
}
on your example:
Map("key1" -> Some("value1"), "key2" -> None, "key3" -> Some("value3")).collectWithSome