If you have a collection of numbers that you want to sum or multiply together, Scala’s collection classes all define both a sum operation and a product operation:
| Array | sum |
product |
|---|---|---|
| List | sum |
product |
| Seq | sum |
product |
| Set | sum |
product |
But what if, for some reason, you want to implement sum or product yourself? Well, it turns out that sum and product are both just special cases of a more general operation that the collection modules also define: reduce. The reduce operation takes all the elements in a collection and combines them in some way to produce a single value. It uses a binary operation to combine the first two values. Then it takes the result of that first combination and combines it with the next element in the collection, and then the next, and so on through the end of the collection.
Here’s an illustration of how to use reduce to implement a product operation:
![Taking a list of [2,5,3,6], reduce multiplies 2 and 5 to produce 10, then 10 and 3 to produce 30, and finally 30 and 6 to get the final result of 180.](https://bradcollins.com/wp-content/uploads/2015/05/scala-reduce-int-product-300x275.png)
The code looks like this:
val product = List(2,5,3,6) reduce {
(x,y) => x * y
}
// product: Int = 180
You can be even more succinct by employing the underscore, Scala’s shorthand for the lambda’s input arguments:
val product = List(2,5,3,6).reduce(_*_) // product: Int = 180
Another special case of reduce is mkString, e.g., List.mkString—also known as a join operation—which allows you to concatenate a sequence of values together into a string with a separator between each value. Here’s how it works:
![Taking a list of strings ["do","mi","sol","do"], reduce combines "do" and "mi" to produce "do-mi", and then combines "do-mi" and "sol" to produce "do-mi-sol", and finally "do-mi-sol" and "do" to produce the final result of "do-mi-sol-do"](https://bradcollins.com/wp-content/uploads/2015/05/scala-reduce-string-concat-1024x554.png)
See how similar it is to the product operation above? If you should want to implement mkString yourself, you could use reduce like this:
val joined =
List("do","mi","sol","do") reduce {
(x,y) => x + "-" + y
}
// joined: String = do-mi-sol-do
Or again, with the shorthand:
val joined =
List("do","mi","sol","do").
reduce(_ + "-" + _)
// joined: String = do-mi-sol-do
Here is the documentation on reduce in each of the collection classes:
Now a few of caveats about reduce:
-
You cannot use
reduceon an empty collection. You will get an exception if you do, so make sure you check to make sure your collection is not empty before you pass it toreduce. (Scala provides an alternative,reduceOption, that does not throw an exception, but represents the result as anOption.) -
The result of a
reduceoperation is always the same type as the elements in the collection. In other words, you can only reduce a collection of typeAto a value of typeA. You cannot reduce a collection of typeAto a value of typeB. -
Finally, order matters if the order of your binary operation matters. In other words, with addition and multiplication, order does not matter. That is,
a + bis the same asb + a. (Mathematicians say that such a function is commutative.) But with something like subtraction, order does matter. In other words,a − bdoes not necessarily produce the same result asb − a. So make sure that your binary operation applies the reduction to its operands in the correct order.
Having noted these caveats, next week, we will cover the fold operation. It operates almost the same way as reduce in that it walks a collection, applying a binary operation to each element. The fold operation cannot help us with that last point—order still matters—but it can handle an empty collection and, if necessary, produce a result that is of a type different from the type of the source collection’s elements.
2 replies on “Scala Saturday – The reduce Method”
Thanks for you post! I like your explanation with images =) . Please, I would like sum of elements in list of list:
val list1 = List((1,2),(3,4),(5,6))
I would like a new list with 3 element:
Result: (1+2, 3+4, 5+6)
Greetings from Peru!!!
Hi, @EJayo.
Here’s what you’re after:
val xs = List((1,2),(3,4),(5,6)) val summed = xs map { x => x._1 + x._2 } // List(3, 7, 11)The
reduceoperation is not what you want here becausereducecombines all of the elements together. The operation you described does not combine all the elements, but rather it modifies each element. That’s not a job forreduce, butmap.Hope this helps!