scala list of objects, using groupBy with average [closed]
basically I'm not really a Java/Scala fan, but unfortunately I'm forced to use it for my studies. Anyways, I was given an assignment:
What the program gets is a list of objects like: Mark(val name String, val style_mark Int, val other_mark Int)
.
How can I use groupBy, to group the marks by name, and get an average for style_mark and other_mark?
Mark("John", 2, 5)
Mark("Peter", 3, 7)
Mark("John", 4, 3)
Should return:
Mark("John", 3, 4)
Mark("Peter", 3, 7)
Thats the code:
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);
}
Any help would be greatly appreciated,
Paul
Just a couple of points here:
You can use pattern matching to avoid all that tedious _1, _2 stuff that comes with tuples.
Underscores in variable/parameter names are a Bad Thing™, they're already used far too heavily elsewhere in the language
So having stated that:
UPDATE: replaced avg
with avgOf
, reducing duplication :)
//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))
}
In the real world, I'd probably pimp Traversable to add the avgOf
method, so you could write v.avgOf(_.styleMark)
, but that would just complicate this example.
As you already said, we can use groupBy
to group the Marks by name. Now we have a Map
where each key is the name and the value is a list of Marks with that name.
We can now iterate over that Map
and replace each key-value pair with a Mark-object that has the key as its name, and the average of the style_mark
s in the list as its style_mark
and the average of other_mark
s in the list as its other_mark
. Like this:
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)))
}
精彩评论