在scala中的GroupBy

时间:2015-11-02 07:18:35

标签: scala

我有

val a = List((1,2), (1,3), (3,4), (3,5), (4,5))

我正在使用A.groupBy(_._1),即groupBy和第一个元素。但是,它给我输出

Map(1 -> List((1,2) , (1,3)) , 3 -> List((3,4), (3,5)), 4 -> List((4,5)))

但是,我想要回答

Map(1 -> List(2, 3), 3 -> List(4,5) , 4 -> List(5))

那么,我该怎么做呢?

5 个答案:

答案 0 :(得分:9)

您可以通过跟踪mapValues(以及每个值map来提取第二个元素)来实现这一点:

scala> a.groupBy(_._1).mapValues(_.map(_._2))
res2: scala.collection.immutable.Map[Int,List[Int]] = Map(4 -> List(5), 1 -> List(2, 3), 3 -> List(4, 5))

答案 1 :(得分:5)

通过模式匹配和Map#withDefaultValue

让生活变得轻松
scala> a.foldLeft(Map.empty[Int, List[Int]].withDefaultValue(Nil)){ 
         case(r, (x, y)) => r.updated(x, r(x):+y) 
       }
res0: scala.collection.immutable.Map[Int,List[Int]] = 
      Map(1 -> List(2, 3), 3 -> List(4, 5), 4 -> List(5))

有两点:

  1. Map#withDefaultValue将获得具有给定默认值的地图,然后您无需检查地图是否包含密钥。

  2. 当scala中的某个地方需要函数值(x1,x2,..,xn) => y时,您总是可以在此处使用匹配case(x1,x2,..,xn) => y的模式,编译器会将其转换为函数auto。请查看8.5 Pattern Matching Anonymous Functions以获取更多信息。

  3. 抱歉我的英语很差。

答案 2 :(得分:2)

作为变体:

<li ng-repeat="post in posts | filter:myFilter">

答案 3 :(得分:1)

您也可以使用foldLeft进行一次迭代。

a.foldLeft(Map.empty[Int, List[Int]])((map, t) => 
  if(map.contains(t._1)) map + (t._1 -> (t._2 :: map(t._1))) 
  else map + (t._1 -> List(t._2)))

scala.collection.immutable.Map[Int,List[Int]] = Map(1 -> List(3, 2), 3 ->
       List(5, 4), 4 -> List(5))

如果列表中元素的顺序很重要,则需要包含reverse

a.foldLeft(Map.empty[Int, List[Int]])((map, t) => 
  if(map.contains(t._1)) (map + (t._1 -> (t._2 :: map(t._1)).reverse)) 
  else map + (t._1 -> List(t._2)))

scala.collection.immutable.Map[Int,List[Int]] = Map(1 -> List(2, 3), 3 ->
       List(4, 5), 4 -> List(5))

答案 4 :(得分:1)

Scala 2.13起,可以使用groupMap 这样您就可以只写:

// val list = List((1, 2), (1, 3), (3, 4), (3, 5), (4, 5))
list.groupMap(_._1)(_._2)
// Map(1 -> List(2, 3), 3 -> List(4, 5), 4 -> List(5))