Search code examples
scalafunctional-programmingscala-cats

Scala merge two maps with different value type


Given two maps

  val m1 = Map("a" -> "a")
  val m2 = Map("a" -> 1)

as a result of merging those two, I would like to get

Map("a" -> ("a",1))

I tried cats semigroupal for this and it pretty much did the job, but I had to create instance for semigroupal by my self, like this

Semigroupal.catsSemigroupalForMap[String]

and then merge them with product method. What do I need to do to not create it manually? E.g. merging two options is straightforward

val noneInt: Option[Int] = None
val some3: Option[Int] = Some(3)
Semigroupal[Option].product(noneInt, some)

I would like to achieve a similar code to merge two maps, but when defining

Semigroupal[Map]

The compiler cannot find any implicity for it.


Solution

  • tupled seems to do the trick:

    @ val mapA = Map(1 -> 2)
    mapA: Map[Int, Int] = Map(1 -> 2)
    
    @ val mapB = Map(1 -> "b")
    mapB: Map[Int, String] = Map(1 -> "b")
    
    @ (mapA, mapB).tupled
    res4: Map[Int, (Int, String)] = Map(1 -> (2, "b"))
    

    And it compiles generically:

    @ def mergeMaps[A, B, C](m1: Map[A, B], m2: Map[A, C]): Map[A, (B, C)] = (m1, m2).tupled
    defined function mergeMaps
    
    @ mergeMaps(mapA, mapB)
    res6: Map[Int, (Int, String)] = Map(1 -> (2, "b"))
    

    It destroys keys that are not in both maps:

    @ val m1 = Map("a" -> "foo", "b" -> "bar")
    m1: Map[String, String] = Map("a" -> "foo", "b" -> "bar")
    
    @ val m2 = Map("a" -> 1, "c" -> 3)
    m2: Map[String, Int] = Map("a" -> 1, "c" -> 3)
    
    @ mergeMaps(m1, m2)
    res9: Map[String, (String, Int)] = Map("a" -> ("foo", 1))