FlatmapValues在地图上

时间:2014-08-17 09:41:20

标签: scala set grouping flatmap

给出Seq个元组,如:

Seq(
  ("a",Set(1,2)),
  ("a",Set(2,3)),
  ("b",Set(4,6)),
  ("b",Set(5,6))
)

我想groupBy然后flatMap获取类似值:

Map(
  b -> Set(4, 6, 5), 
  a -> Set(1, 2, 3)
)

我的第一个实现是:

Seq(
  ("a" -> Set(1,2)),
  ("a" -> Set(2,3)),
  ("b" -> Set(4,6)),
  ("b" -> Set(5,6))
) groupBy (_._1) mapValues (_ map (_._2)) mapValues (_.flatten.toSet)

我想知道是否有更有效,也许更简单的方法来实现这一结果。

3 个答案:

答案 0 :(得分:4)

我会使用foldLeft,我认为它更具可读性,你可以避免使用groupBy

val r = Seq(
    ("a",Set(1,2)),
    ("a",Set(2,3)),
    ("b",Set(4,6)),
    ("b",Set(5,6))
  ).foldLeft(Map[String, Set[Int]]()){
    case (seed,(k,v)) => {
      seed.updated(k,v ++ seed.getOrElse(k,Set[Int]()))
    }
  }

答案 1 :(得分:4)

您走在正确的轨道上,但您可以使用一个mapValues并结合mapflatten来简化一点:

val r = Seq(
  ("a" -> Set(1,2)),
  ("a" -> Set(2,3)),
  ("b" -> Set(4,6)),
  ("b" -> Set(5,6))
).groupBy(_._1).mapValues(_.flatMap(_._2).toSet)

我实际上发现这比foldLeft版本更具可读性(但请注意mapValues会返回a non-strict collection,这可能是您想要的,也可能不是。“

答案 2 :(得分:0)

@grotrianster回答可以使用Set和Map的半群二进制运算|+|进行优化:

import scalaz.syntax.semigroup._
import scalaz.std.map._
import scalaz.std.set._

Seq(
  ("a",Set(1,2)),
  ("a",Set(2,3)),
  ("b",Set(4,6)),
  ("b",Set(5,6))
).foldLeft(Map[String, Set[Int]]()){case (seed, (k, v)) => seed |+| Map(k -> v)}

使用reduce代替fold

Seq(
  ("a", Set(1, 2)),
  ("a", Set(2, 3)),
  ("b", Set(4, 6)),
  ("b", Set(5, 6))
).map(Map(_)).reduce({_ |+| _})

SetMap视为Monoids:

Seq(
  ("a", Set(1, 2)),
  ("a", Set(2, 3)),
  ("b", Set(4, 6)),
  ("b", Set(5, 6))
).map(Map(_)).toList.suml