这里有两种变体:
val p = List(("a", List("x","y","z")), ("b", List("x")), ("c", List("y","z")))
// 1. "Transducers"
p.flatMap{ case (k, v) => v.map { _ -> k } } // List((x,a), (y,a), (z,a), (x,b), (y,c), (z,c))
.groupBy(_._1) // Map(z -> List((z,a), (z,c)), y -> List((y,a), (y,c)), x -> List((x,a), (x,b)))
.mapValues(_.map(_._2)) // Map(z -> List(a, c), y -> List(a, c), x -> List(a, b))
// 2. For-loop
var res = Map[String, List[String]]()
for ( (k, vs) <- p; v <- vs) {
res += v -> k :: res.getOrElse(v, List())
}
res // Map(x -> List(b, a), y -> List(c, a), z -> List(c, a))
// Note, values of `res` are inverted,
// because the efficient "cons" operator (::) was used to add values to the lists
// you can revert the lists afterwards as this:
res.mapValues(_.reverse) // Map(x -> List(a, b), y -> List(a, c), z -> List(a, c))
第二个变体性能更高,因为没有创建中间集合,但它也可以被认为是“不那么惯用的”,因为使用了可变变量res。但是,在私有方法中使用可变方法是完全可以的。
UPD。根据@LuisMiguelMejíaSuárez 的建议:
在(1)中,由于scala 2.13,groupBy后面跟mapValues可以替换为groupMap,所以整个链条变成:
p.flatMap{ case (k, v) => v.map { _ -> k } }
.groupMap(_._1)(_._2)
另一个没有中间集合的功能变体可以使用foldLeft实现:
p.foldLeft(Map[String, List[String]]()) {
case (acc, (k, vs)) =>
vs.foldLeft(acc) { (acc1, v) =>
acc1 + (v -> (k :: acc1.getOrElse(v, List())))
}
}
或者使用updatedWith(scala 2.13)更有效:
p.foldLeft(Map[String, List[String]]()) {
case (acc, (k, vs)) =>
vs.foldLeft(acc) { (acc1, v) =>
acc1.updatedWith(v) {
case Some(list) => Some(k :: list)
case None => Some(List(k))
}
}
}
...或者同样的东西稍微短一点:
p.foldLeft(Map[String, List[String]]()) {
case (acc, (k, vs)) =>
vs.foldLeft(acc) { (acc1, v) =>
acc1.updatedWith(v)(_.map(k :: _).orElse(Some(List(k))))
}
}
总的来说,我建议根据您的目标使用foldLeft 变体(性能和功能最高)或第一个groupMap 变体(更短,可以说更具可读性,但性能较差)。