listscalacombinationscartesian-productcartesian

Expand Map[String,List[String]] into Cartesian Product in Scala


I'm looking for a version of Expand a Set[Set[String]] into Cartesian Product in Scala based on map type :

I would like to start with :

val values = Map(
      "id" -> List("Paul","Joe"),
      "number" -> List("1","2","3"),
      "Type" -> List("A","B","C")
    )

And compute :

val final = Set(
Map("id" -> "Paul", "number" -> "1", "Type" -> "A" ),
Map("id" -> "Paul", "number" -> "1", "Type" -> "B" ),
Map("id" -> "Paul", "number" -> "1", "Type" -> "C" ),
Map("id" -> "Paul", "number" -> "1", "Type" -> "A" ),
Map("id" -> "Paul", "number" -> "1", "Type" -> "B" ),
Map("id" -> "Paul", "number" -> "1", "Type" -> "C" ),
Map("id" -> "Paul", "number" -> "2", "Type" -> "A" ),
Map("id" -> "Paul", "number" -> "2", "Type" -> "B" ),
Map("id" -> "Paul", "number" -> "2", "Type" -> "C" ),
....
Map("id" -> "Joe", "number" -> "3", "Type" -> "B" ),
Map("id" -> "Joe", "number" -> "3", "Type" -> "C" )
)

I try to transform the following code

def combine[A](xs: Traversable[Traversable[A]]): Seq[Seq[A]] =
     xs.foldLeft(Seq(Seq.empty[A])){
     (x, y) => for (a <- x.view; b <- y) yield a :+ b }

but I have some issue to understand how to construct all the map to add in the big Set()


Solution

  • You could do something like this:
    (but note that for larger maps this would consume a lot of memory, it may be worth looking into generating a LazyList instead)

    def cartesianProductMap[A, B](data: Map[A, List[B]]): List[Map[A, B]] =
      data.foldLeft(Map.empty[A, B] :: Nil) {
        case (acc, (key, values)) =>
          values.flatMap { b =>
            acc.map(_ + (key -> b))
          }
      }
    

    Code running in Scastie.


    BTW, if you use cats you could just do: values.to(SortedMap).sequence