scala列表中的对象,使用groupBy和average

时间:2010-10-22 15:19:10

标签: list scala group-by

基本上我不是真正的Java / Scala粉丝,但不幸的是我被迫用它来学习。无论如何,我得到了一个任务:

程序得到的是一个对象列表,如:Mark(val name String, val style_mark Int, val other_mark Int)

如何使用groupBy,按名称对标记进行分组,并获得style_mark和other_mark的平均值?

Mark("John", 2, 5)
Mark("Peter", 3, 7)
Mark("John", 4, 3)

应该返回:

Mark("John", 3, 4)
Mark("Peter", 3, 7)

这是代码:

class Mark(val name: String, val style_mark: Int, val other_mark: Int) {}

object Test extends Application
  {
  val m1 = new Mark("Smith", 18, 16);
  val m2 = new Mark("Cole", 14, 7);
  val m3 = new Mark("James", 13, 15);
  val m4 = new Mark("Jones", 14, 16);
  val m5 = new Mark("Richardson", 20, 19);
  val m6 = new Mark("James", 4, 18);

  val marks = List(m1, m2, m3, m4, m5, m6);

  def avg(xs: List[Int]) = xs.sum / xs.length

  marks.groupBy(_.name).map { kv => Mark(kv._1, avg(kv._2.map(_.style_mark)), avg(kv._2.map(_.other_mark))) }

  println(marks);
  }

非常感谢任何帮助,

2 个答案:

答案 0 :(得分:6)

这里只有几点:

  1. 您可以使用模式匹配来避免元组附带的所有繁琐的_1,_2内容。

  2. 变量/参数名称中的下划线是Bad Thing™,它们已经在语言的其他地方使用得太多了

  3. 所以说:

    更新:avg替换为avgOf,减少重复:)

    //Needs two param lists so that inference will work properly
    //when supplying the closure
    def avgOf[T](xs:List[T])(f:(T)=>Int) = xs.map(f).sum / xs.length
    
    marks.groupBy(_.name).map {
      case (k,v) => new Mark(k, avgOf(v)(_.styleMark), avgOf(v)(_.otherMark))
    }
    

    在现实世界中,我可能会通过pimp Traversable添加avgOf方法,因此您可以编写v.avgOf(_.styleMark),但这只会使此示例复杂化。

答案 1 :(得分:3)

正如您已经说过的,我们可以使用groupBy按名称对标记进行分组。现在我们有一个Map,其中每个键都是名称,值是带有该名称的标记列表。

我们现在可以迭代Map并将每个键值对替换为具有键作为其名称的Mark对象,以及列表中style_mark的平均值作为其style_mark以及列表中other_mark的平均值为other_mark。像这样:

def avg(xs: List[Int]) = xs.sum / xs.length
marks.groupBy(_.name).map { kv =>
  Mark(kv._1, avg(kv._2.map(_.style_mark)), avg(kv._2.map(_.other_mark)))
}