1. Overview
In this tutorial, we’ll show how to calculate the average of a List using only the Scala standard library.
2. Traversing the List
Let’s start by assuming the List contains only numbers. Otherwise, the average definition would not make much sense.
Our first approach is very naive: we are going to iterate through the List and sum everything:
def naive(lst: List[Int]): Int = {
var total = 0
var nrElements = 0
for (elem <- lst) {
total += elem
nrElements += 1
}
total / nrElements
}
naive(List(1, 2, 3, 4)) shouldBe 2
Consider that the result will be rounded because we are dealing with Int. If we want to have a more exact calculation, we need to convert it into Double before doing any math operations:
def averageDouble(lst: List[Int]): Double = {
var total = 0
// here we use double to ensure we dont round final number
var nrElements = 0.0
for (elem <- lst) {
total += elem
nrElements += 1
}
total / nrElements
}
averageDouble(List(1, 2, 3, 4)) shouldBe 2.5
Note that floating point numbers are a common source of bugs, but we will leave that outside of the scope of this article.
3. Using List Methods
We can further simplify the previous solution by making use of the existing Scala List methods to get the sum and size of the List:
def averageWithListMethods(lst: List[Int]): Int = {
lst.sum / lst.size
}
averageWithListMethods(List(1, 2, 3, 4)) shouldBe 2
Once again note that we need to convert into Double to ensure the final value doesn’t get rounded:
def averageWithListMethodsDouble(lst: List[Int]): Double = {
lst.sum / lst.size.toDouble
}
averageWithListMethodsDouble(List(1, 2, 3, 4)) shouldBe 2.5
4. Using List.fold()
The previous solution works great, but it has a small performance issue: each one of the methods we used will traverse the List once. This means we do that twice, while it could be done in a single one, which may become a bottleneck for really long lists. To avoid this, we can use the List.fold method to compute both, transversing the List just once:
def averageWithFold(lst: List[Int]): Double = {
val (sum, size) =
lst.foldLeft((0.0, 0))((pair, elem) => (pair._1 + elem, pair._2 + 1))
sum / size
}
averageWithFold(List(1, 2, 3, 4)) shouldBe 2.5
In this solution, we only traverse the List once by using the List.foldLeft method, which allows us to carry the accumulated values as we iterate through the list. These values are carried as a tuple, where the first is the current sum and the second the size so far. For each element we process, we add its value to the sum and increase the size. At the end of it, we will have the total sum of all list elements, as well as the size of the list with a single iteration.
5. Conclusion
In this article, we learned how to calculate the average of a List in Scala.
We saw how to compute the average of a List using a naive approach with loops and utility List methods. Finally, we saw a more performant solution using the List.foldLeft method, which allows us to compute the average of the List in a single traversal.
As always, the sample code used in this article is available over on GitHub.