Scala - calculate average of SomeObj.double in a List[SomeObj]

Viewed 43336

I'm on my second evening of scala, and I'm resisting the urge to write things in scala how I used to do them in java and trying to learn all of the idioms. In this case I'm looking to just compute an average using such things as closures, mapping, and perhaps list comprehension. Irrespective of whether this is the best way to compute an average, I just want to know how to do these things in scala for learning purposes only

Here's an example: the average method below is left pretty much unimplemented. I've got a couple of other methods for looking up the rating an individual userid gave that uses the find method of TraversableLike (I think), but nothing more that is scala specific, really. How would I compute an average given a List[RatingEvent] where RatingEvent.rating is a double value that I'd to compute an average of across all values of that List in a scala-like manner?.

package com.brinksys.liftnex.model

class Movie(val id : Int, val ratingEvents : List[RatingEvent]) {

    def getRatingByUser(userId : Int) : Int =  {
        return getRatingEventByUserId(userId).rating
    }

    def getRatingEventByUserId(userId : Int) : RatingEvent = {
        var result = ratingEvents find {e => e.userId == userId }
        return result.get
    }

    def average() : Double = {
        /* 
         fill in the blanks where an average of all ratingEvent.rating values is expected
        */
       return 3.8
    }
}

How would a seasoned scala pro fill in that method and use the features of scala to make it as concise as possible? I know how I would do it in java, which is what I want to avoid.

If I were doing it in python, I assume the most pythonic way would be:

sum([re.rating. for re in ratingEvents]) / len(ratingEvents)

or if I were forcing myself to use a closure (which is something I at least want to learn in scala):

reduce(lambda x, y : x + y, [re.rating for re in ratingEvents]) / len(ratingEvents)

It's the usage of these types of things I want to learn in scala.

Your suggestions? Any pointers to good tutorials/reference material relevant to this are welcome :D

5 Answers

Tail recursive solution can achieve both single traversal and avoid high memory allocation rates

def tailrec(input: List[RatingEvent]): Double = {
  @annotation.tailrec def go(next: List[RatingEvent], sum: Double, count: Int): Double = {
    next match {
      case Nil => sum / count
      case h :: t => go(t, sum + h.rating, count + 1)
    }
  }
  go(input, 0.0, 0)
}

Here are jmh measurements of approaches from above answers on a list of million elements:

[info] Benchmark                                Mode              Score     Units
[info] Mean.foldLeft                            avgt              0.007     s/op
[info] Mean.foldLeft:·gc.alloc.rate             avgt           4217.549     MB/sec
[info] Mean.foldLeft:·gc.alloc.rate.norm        avgt       32000064.281     B/op
...
[info] Mean.mapAndSum                           avgt              0.039     s/op
[info] Mean.mapAndSum:·gc.alloc.rate            avgt           1690.077     MB/sec
[info] Mean.mapAndSum:·gc.alloc.rate.norm       avgt       72000009.575     B/op
...
[info] Mean.tailrec                             avgt              0.004     s/op
[info] Mean.tailrec:·gc.alloc.rate              avgt             ≈ 10⁻⁴     MB/sec
[info] Mean.tailrec:·gc.alloc.rate.norm         avgt              0.196     B/op

Related