开发者

scala list of objects, using groupBy with average [closed]

开发者 https://www.devze.com 2023-01-21 04:09 出处:网络
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time,or an extraordinarily narrow situation that is not generally applic
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broad开发者_开发知识库ly applicable, visit the help center. Closed 10 years ago.

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:

  1. You can use pattern matching to avoid all that tedious _1, _2 stuff that comes with tuples.

  2. 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_marks in the list as its style_mark and the average of other_marks 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)))
}
0

精彩评论

暂无评论...
验证码 换一张
取 消