Jessé Monguar
Jessé Monguar

Reputation: 27

Reverse a Map which has A Set as its value using HOF

I am trying to reverse a map that has a String as the key and a set of numbers as its value

My goal is to create a list that contains a tuple of a number and a list of strings that had the same number in the value set

I have this so far:

def flipMap(toFlip: Map[String, Set[Int]]): List[(Int, List[String])] = {
  toFlip.flatMap(_._2).map(x => (x, toFlip.keys.toList)).toList
}

but it is only assigning every String to every Int

val map = Map(
 "A" -> Set(1,2),
 "B" -> Set(2,3)
)

should produce: List((1, List(A)), (2, List(A, B)), (3, List(B)))

but is producing: List((1, List(A, B)), (2, List(A, B)), (3, List(A, B)))

Upvotes: 0

Views: 139

Answers (3)

You can do something like this:

def flipMap(toFlip: Map[String, Set[Int]]): List[(Int, List[String])] =
  toFlip
    .toList
    .flatMap {
      case (key, values) =>
        values.map(value => value -> key)
    }.groupMap(_._1)(_._2)
    .view
    .mapValues(_.distinct)
    .toList

Note, I personally would return a Map instead of a List


Or if you have cats in scope.

def flipMap(toFlip: Map[String, Set[Int]]): Map[Int, Set[String]] =
  toFlip.view.flatMap {
    case (key, values) =>
      values.map(value => Map(value -> Set(key)))
  }.toList.combineAll

Upvotes: 1

Eastsun
Eastsun

Reputation: 18859

// both scala2 & scala3
scala> map.flatten{ case(k, s) => s.map(v => (k, v)) }.groupMapReduce{ case(k, v) => v }{case(k, v) => List(k)}{ _ ++ _ }
val res0: Map[Int, List[String]] = Map(1 -> List(A), 2 -> List(A, B), 3 -> List(B))

// scala3 only
scala> map.flatten((k, s) => s.map(v => (k, v))).groupMapReduce((k, v) => v)((k, v) => List(k))( _ ++ _ )
val res1: Map[Int, List[String]] = Map(1 -> List(A), 2 -> List(A, B), 3 -> List(B))

Upvotes: 0

AminMal
AminMal

Reputation: 3173

This works to, but it's not exactly what you might need and you may need some conversions to get the exact data type you need:

toFlip.foldLeft(Map.empty[Int, Set[String]]) {
    case (acc, (key, numbersSet)) =>
      numbersSet.foldLeft(acc) {
        (updatingMap, newNumber) =>
          updatingMap.updatedWith(newNumber) {
            case Some(existingSet) => Some(existingSet + key)
            case None => Some(Set(key))
          }
      }
  }

I used Set to avoid duplicate key insertions in the the inner List, and used Map for better look up instead of the outer List.

Upvotes: 1

Related Questions