Soumya Simanta
Soumya Simanta

Reputation: 11741

Functional way to create a union of all keys from a Seq of Maps in Scala

I've a Sequence of Maps

 Seq[Map[String,Int]] 

I want to create a Seq/Set that is the Union of all keys in each of the Map.

unionallkeys = ( "a1", "a2", "a3", "b1", "b2", "b3", "c1", "c2", "c3" ) 

in the example below.

scala> val a = Map( ("a1", 1), ("a2", 2), ("a3", 3) ) 
a: scala.collection.immutable.Map[String,Int] = Map(a1 -> 1, a2 -> 2, a3 -> 3)

scala> val b = Map( ("b1", 1), ("b2", 2), ("b3", 3) ) 
b: scala.collection.immutable.Map[String,Int] = Map(b1 -> 1, b2 -> 2, b3 -> 3)

scala> val c = Map( ("c1", 1), ("c2", 2), ("c3", 3) ) 
c: scala.collection.immutable.Map[String,Int] = Map(c1 -> 1, c2 -> 2, c3 -> 3)

scala> val misc = Map( ("a1", 1), ("b2", 2), ("c3", 3) )
misc: scala.collection.immutable.Map[String,Int] = Map(a1 -> 1, b2 -> 2, c3 -> 3)
                             ^

scala> val rows = List(a,b,c,misc)
rows: List[scala.collection.immutable.Map[String,Int]] = List(Map(a1 -> 1, a2 -> 2, a3 -> 3), Map(b1 -> 1, b2 -> 2, b3 -> 3), Map(c1 -> 1, c2 -> 2, c3 -> 3), Map(a1 -> 1, b2 -> 2, c3 -> 3))

Upvotes: 2

Views: 1590

Answers (2)

Juh_
Juh_

Reputation: 15539

Travis solution is immutable which is better in term of design.

Let me just provide a solution using mutable Set:

def keysUnion[A,B](maps: Seq[Map[A,B]]): scala.collection.Set[A] = {
  val allKeys = scala.collection.mutable.Set[A]()
  maps.foreach( m => allKeys ++= m.keySet )
  allKeys
}


// test with some random input data
import scala.util.Random
val maps = (1 to 10).map( i => (1 to i).map( k => k -> k ).toMap )

keysUnion(maps)

I don't know if the mutable.Set is efficient. And most probably the .toSet of the immutable solution does the same. So for simple case it should be very similar, but it should be better if the flatten list of keys is (really) big.

Note: when using mutable collection, it's better to hide it inside method that output a trait with immutable API. This is why the solution I provided is in the form of a function which output scala.collection.Set.

Upvotes: 0

Travis Brown
Travis Brown

Reputation: 139038

You can write this as a fairly clear one-liner:

scala> val keys: Set[String] = rows.flatMap(_.keySet).toSet
keys: Set[String] = Set(c3, b2, b3, c2, b1, c1, a3, a1, a2)

Let's break this down step by step. First of all, the keySet method on a map will give you a set of the map's keys. If you wanted a list of sets of keys, you could just write the following:

scala> val keySets: List[Set[String]] = rows.map(_.keySet)
keySets: List[Set[String]] = List(Set(a1, a2, a3), Set(b1, b2, b3), ...

You could then flatten the nested structure:

scala> val keyList: List[String] = keySets.flatten
keyList: List[String] = List(a1, a2, a3, b1, b2, b3, c1, c2, c3, a1, b2, c3)

Note that the result has the type of the outer collection—i.e. it's a list, not a set. You can convert it to a set with toSet:

scala> val keys: Set[String] = keyList.toSet
keys: Set[String] = Set(c3, b2, b3, c2, b1, c1, a3, a1, a2)

And you're done. The first line above just does all of these steps at once, and combines the map and flatten calls into a single flatMap.

Upvotes: 7

Related Questions