基本上我不是真正的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);
}
非常感谢任何帮助,
保
答案 0 :(得分:6)
这里只有几点:
您可以使用模式匹配来避免元组附带的所有繁琐的_1,_2内容。
变量/参数名称中的下划线是Bad Thing™,它们已经在语言的其他地方使用得太多了
所以说:
更新:将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)))
}