'Combine two Maps with same key type, but different value type in scala

I want to combine two Maps with the same type of key, but different type of value.

The result should have values containing with both value types (optional, because some values could be present in only one of the input Maps)

The type anotation is

def combineMaps[T, U, V](map1: Map[T, U], map2: Map[T, V]): Map[T, (Option[U], Option[V])] = {
  ???
}

I know it could be achieved with complicated code like:

(map1.mapValues(Some(_) -> None).toList ++ map2.mapValues(None -> Some(_)).toList) // List[(T, (Option[U], Option[V]))]
      .groupBy(_._1)                                                               // Map[T, List[(T, (Option[U], Option[V]))]]
      .mapValues(_.map(_._2))                                                      // Map[T, List[(Option[U], Option[V])]]
      .mapValues { list => (
        list.collectFirst { case (Some(u), _) => u },
        list.collectFirst { case (_, Some(v)) => v }
      ) }                                                                          // Map[T, (Option[U], Option[V])]

Although the code is working, it does not benefit from the fact that every key in a Map is present only once. Method .toList drop this type information.

I am looking for some elegant scala way to do it (possibly with cats/scalaz, but best without them)



Solution 1:[1]

A union of the keySets followed by mapping the keys to tuples of gets should do it:

def combineMaps[T, U, V](m1: Map[T, U], m2: Map[T, V]): Map[T, (Option[U], Option[V])] =
  (m1.keySet union m2.keySet).map(k => (k, (m1.get(k), m2.get(k)))).toMap

Example:

combineMaps(Map('a'->1, 'b'->2, 'c'->3), Map('a'->10.0, 'c'->30.0, 'd'->40.0))
// res1: Map[Char,(Option[Int], Option[Double])] = Map(
//   a -> (Some(1),Some(10.0)), b -> (Some(2),None), c -> (Some(3),Some(30.0)), d -> (None,Some(40.0))
// )

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Leo C