Scala flatten nested map - scala

I have a nested Map like this one:
Map(1 -> Map(2 -> 3.0, 4 -> 5.0), 6 -> Map(7 -> 8.0))
I would like to 'flatten' it in a way such that the keys of the outer and inner maps are paired, i.e. for the example above:
Seq((1,2),(1,4),(6,7))
what is an elegant way to do this?

val m = Map(1 -> Map(2 -> 3.0, 4 -> 5.0), 6 -> Map(7 -> 8.0))
m.toSeq.flatMap({case (k, v) => v.keys.map((k,_))})

With for-comprehension:
val m = Map(1 -> Map(2 -> 3.0, 4 -> 5.0), 6 -> Map(7 -> 8.0))
scala> for((k1, v1) <- m.toSeq; k2 <- v1.keys) yield (k1, k2)
res4: Seq[(Int, Int)] = ArrayBuffer((1,2), (1,4), (6,7))

Related

Reduce rdd of maps

I have and rdd like that :
Map(A -> Map(A1 -> 1))
Map(A -> Map(A2 -> 2))
Map(A -> Map(A3 -> 3))
Map(B -> Map(B1 -> 4))
Map(B -> Map(B2 -> 5))
Map(B -> Map(B3 -> 6))
Map(C -> Map(C1 -> 7))
Map(C -> Map(C2 -> 8))
Map(C -> Map(C3 -> 9))
I need to have the same rdd reduced by key and having as many values as it has previously:
Map(A -> Map(A1 -> 1, A2 -> 2, A3 -> 3))
Map(B -> Map(B1 -> 4, B2 -> 5, B3 -> 6))
Map(C -> Map(C1 -> 7, C2 -> 8, C3 -> 9))
I tried with a reduce:
val prueba = replacements_2.reduce((x,y) => x ++ y)
But only remains the value of the last element evaluated with the same key:
(A,Map(A3 -> 3))
(C,Map(C3 -> 9))
(B,Map(B3 -> 6))
I think you should model your data differently, your Map approach seems a bit awkward. Why represent 1 entry by a Map with 1 element? A Tuple2 is more suitable for this... Anyway, you need reduceByKey. To do this, you first need to convert your rdd to a key-value RDD:
rdd
.map(m => (m.keys.head,m.values.head)) // create key-value RDD
.reduceByKey((a,b) => a++b) // merge maps
.map{case (k,v) => Map(k -> v)} // create Map again

Scala Map update

I want to update Map value which is present in another Map. When I try to update is says 'value update is not a member of Option[scala.collection.immutable.Map[Int,Int]]'.
I tried to convert the value to Map but still, it didn't work for me.
val map = Map("one" -> Map(1 -> 11), "two" -> Map(2 -> 22))
val value = map1.get("one")
value(1) = 100 //value update is not a member of Option[scala.collection.Map[Int,Int]]
There are two mistakes you are making.
Calling get on a Map will return an Option, hence you are not able to set the value.
You are using immutable Map when your operation/purpose is to update the value of some key, for which you need to use mutable map.
Let us try to do the write some snippets to solve these two problems.
scala> val map = Map("one" -> Map(1 -> 11), "two" -> Map(2 -> 22))
map: scala.collection.immutable.Map[String,scala.collection.immutable.Map[Int,Int]] = Map(one -> Map(1 -> 11), two -> Map(2 -> 22))
scala> val valueOption = map.get("one")
valueOption: Option[scala.collection.immutable.Map[Int,Int]] = Some(Map(1 -> 11))
scala> val value = map("one")
value: scala.collection.immutable.Map[Int,Int] = Map(1 -> 11)
scala> value(1) = 100
<console>:13: error: value update is not a member of scala.collection.immutable.Map[Int,Int]
value(1) = 100
You should notice the difference between getting the value using .get and directly using parenthesis. This is a more understandable error and no need to understand Scala magic happening underneath.
Now if you repeat the same statements after importing mutable Map, you will be able to get what you are trying to achieve.
scala> import scala.collection.mutable.Map
import scala.collection.mutable.Map
scala> val map = Map("one" -> Map(1 -> 11), "two" -> Map(2 -> 22))
map: scala.collection.mutable.Map[String,scala.collection.mutable.Map[Int,Int]] = Map(one -> Map(1 -> 11), two -> Map(2 -> 22))
scala> val value = map("one")
value: scala.collection.mutable.Map[Int,Int] = Map(1 -> 11)
scala> value(1) = 100
scala> map
res2: scala.collection.mutable.Map[String,scala.collection.mutable.Map[Int,Int]] = Map(one -> Map(1 -> 100), two -> Map(2 -> 22))
When you created you first map it is already immutable which cannot be changed
scala> val map = Map("one" -> Map(1 -> 11), "two" -> Map(2 -> 22))
map: scala.collection.immutable.Map[String,scala.collection.immutable.Map[Int,Int]] = Map(one -> Map(1 -> 11), two -> Map(2 -> 22))
Your second command is returning an Option of immutable Map again and that can not be updated too.
scala> val value = map.get("one")
value: Option[scala.collection.immutable.Map[Int,Int]] = Some(Map(1 -> 11))
As chunjef suggested, you should be using mutable Map
scala> val map = Map("one" -> scala.collection.mutable.Map(1 -> 11), "two" -> scala.collection.mutable.Map(2 -> 22))
map: scala.collection.immutable.Map[String,scala.collection.mutable.Map[Int,Int]] = Map(one -> Map(1 -> 11), two -> Map(2 -> 22))

scala convert List of map to map

I have a simple question
I have List of Map like this
List(
Map("a" -> "a"),
Map("b" -> "b")
)
And I want the result like this
Map(
"a"->"a",
"b"->"b"
)
It can be overwrite if the key is duplication
Any one please help me
You can combine flatten and toMap:
val list = List(Map("k1" -> "v1", "k2" -> "v2"))
list.flatten.toMap // Map(k1 -> v1, k2 -> v2)
flatten will convert the list of maps into a list of tuples and then toMap will convert your list of tuples into a map.
You can try using reduce:
scala> val list = List(Map("k1" -> "v1", "k2" -> "v2"))
list: List[scala.collection.immutable.Map[String,String]] = List(Map(k1 -> v1, k2 -> v2))
scala> list.reduce(_ ++ _)
res0: scala.collection.immutable.Map[String,String] = Map(k1 -> v1, k2 -> v2)
scala> val list = List(Map("k1" -> "v1"), Map("k2" -> "v2"))
list: List[scala.collection.immutable.Map[String,String]] = List(Map(k1 -> v1), Map(k2 -> v2))
scala> list.reduce(_ ++ _)
res1: scala.collection.immutable.Map[String,String] = Map(k1 -> v1, k2 -> v2)
This way you needn't convert to any intermediate data type.

Reverse a map of type [Int, Seq[Int]]

I need to reverse a map
customerIdToAccountIds:Map[Int, Seq[Int]]
such that each account ID is a key to a list of all the customer IDs of the account (many-to-many relationship):
accountIdToCustomerIds:Map[Int, Seq[Int]]
What is a good idiomatic way to accomplish this? Thanks!
Input:
val customerIdToAccountIds:Map[Int, Seq[Int]] = Map(1 -> Seq(5,6,7), 2 -> Seq(5,6,7), 3 -> Seq(5,7,8))
val accountIdToCustomerIds:Map[Int, Seq[Int]] = ???
1 -> Seq(5,6,7)
2 -> Seq(5,6,7)
3 -> Seq(5,7,8)
Output:
5 -> Seq(1,2,3)
6 -> Seq(1,2)
7 -> Seq(1,2,3)
8 -> Seq(3)
val m = Map( 1 -> Seq(5,6,7)
, 2 -> Seq(5,6,7)
, 3 -> Seq(5,7,8) )
// Map inverter: from (k -> List(vs)) to (v -> List(ks))
m flatten {case(k, vs) => vs.map((_, k))} groupBy (_._1) mapValues {_.map(_._2)}
//result: Map(8 -> List(3), 5 -> List(1, 2, 3), 7 -> List(1, 2, 3), 6 -> List(1, 2))
val customerIdToAccountIds = Map(1 -> Seq(5, 6, 7), 2 -> Seq(5, 6, 7), 3 -> Seq(5, 7, 8))
val accountIdToCustomerIds = customerIdToAccountIds.toSeq.flatMap {
case (customerId, accountIds) => accountIds.map { accountId => (customerId, accountId) } // swap
}.groupBy(_._2).mapValues(_.map(_._1)) // groupBy accountId and extract customerId from tuples

Remove an entry from a Map and return a new Map

I want to check if a Map doesn't contain empty values. If the value is empty it shouldn't includen in the new Map.
I tried something like:
val newmap = map.map{ entry => if(!entry._2.isEmpty()) Map(entry._1 -> entry._2)}
This does exactly do what I want, but it is not very nice. Is there a better solution?
scala> Map(1 -> List(3, 4), 2 -> Nil, 3 -> List(11))
res2: scala.collection.immutable.Map[Int,List[Int]] = Map(1 -> List(3, 4), 2 -> List(), 3 -> List(11))
scala> res2.filter(_._2.nonEmpty)
res3: scala.collection.immutable.Map[Int,List[Int]] = Map(1 -> List(3, 4), 3 -> List(11))
scala>
You mean empty as in null?
scala> val map = collection.immutable.HashMap[Int, String] (1 -> "a", 2-> "b", 3 -> null)
map: scala.collection.immutable.HashMap[Int,String] = Map(1 -> a, 2 -> b, 3 -> null)
scala> val newmap=map filter (_._2 != null)
newmap: scala.collection.immutable.HashMap[Int,String] = Map(1 -> a, 2 -> b)
EDIT: dang... #missingfaktor beat me to it... :)