1. Introduction
In the following tutorial, we’ll look at the difference between foldLeft and reduceLeft in Scala.
foldLeft and reduceLeft are both functions that traverse collections starting from the first value of the collection. A function is applied to each element, and an accumulator value is passed to each next execution of the function. foldLeft and reduceLeft behave in the same way as a recursive function.
2. Method Signatures
Let’s have a look at the method signatures of foldLeft and reduceRight to learn how they behave.
2.1. foldLeft Signature
foldLeft traverses a collection applying a function to each element that can return any type that is specified by the initial seed value.
Here’s the signature for foldLeft:
def foldLeft[B](z: B)(op: (B, A) ⇒ B): B
We can see from the foldLeft signature that the method expects an initial value of the same type as the return type. Our initial value z will be the starting point for the accumulator for our function. A is the type of the elements in the collection we’re traversing and has no type constraint related to the return type B.
2.2. reduceLeft Signature
reduceLeft is used to reduce a collection by applying a function to each element in order to combine them and return a single result.
Let’s have a look at the reduceLeft signature:
def reduceLeft[B >: A](op: (B, A) ⇒ B): B
At first glance, it looks quite similar to foldLeft, but there are two differences to look at:
- There is no initial value in reduceLeft. This means that the initial value for the accumulator will be the first value in the collection that we’d like to reduce
- The “reduce” implies that we expect to decrease or shrink our collection. We can see that the intent is to reduce by looking at the bounds applied to A and B ([B >: A]). In reduceLeft, there is a lower type bound relationship between A and B. This means B must be a supertype of A
3. Practical Examples
We’ll have a look at some practical examples of foldLeft and reduceLeft in action to illustrate their differences.
3.1. foldLeft Example
One of the powerful things about foldLeft is the flexibility it has in transforming a collection. There are no type bounds on the input and result type, so it can transform data into a new collection.
To illustrate this, let’s take an example of a system with many people in it.
A person in our system will have a name and an age:
case class Person(name: String, age: Int)
Let’s say the representation we have of the people in the system is a map of an ID and the person:
val users: Map[Int, Person] =
Map(1 -> Person("Tom", 10), 2 -> Person("Gillian", 13), 3 -> Person("Sarah", 17), 4 -> Person("David", 20))
Next, we’d like to compile a list of the users but no longer care about their ID. We’d like to transform the Map[Int, Person]
to a List[Person]
.
We can use foldLeft for the following:
val peopleList: List[Person] = people.foldLeft(List.empty[Person])((people, current) => {
people :+ current._2
}
assert(peopleList == List(Person("Tom", 10), Person("Gillian", 13), Person("Sarah", 17), Person("David", 20)))
3.2. reduceLeft Example
Now that we have the list of users, we’d like to extract some useful information, such as who is the youngest user. We’ll reduce the list of users to a single user who has the lowest age:
val youngestPerson: Person = people.reduceLeft((youngestPerson, currentPerson) => {
if (youngestPerson.age > currentPerson.age) {
currentPerson
else {
youngestPerson
})
assert(youngestPerson == Person("Tom", 10))
In the above example, we reduced the List[Person] to a Person with the youngest age.
4. Conclusion
In this tutorial, we looked at the difference between foldLeft and reduceLeft in Scala. As always, the code is over on GitHub.