Reduce, Reduce, Reduce

After my last article on map, filter, and reduce there was an outpour of requests to do an article on just reduce. This time let’s dive into all the fun things you can do with reduce.

Reduce, Reduce, Reduce

After my last article on map, filter, and reduce there was an outpour of requests to do an article on just reduce. This time let’s dive into all the fun things you can do with reduce. Before we do that though, let’s make sure we really understand how it works.

Why’s it Called Reduce?

I find it helpful if we look at the dictionary definition for the word reduce [1]

a: To draw together or cause to converge, or consolidate
Example: reduce all the questions to one

b: To diminish in size, amount, extent, or number
Example: reduce taxes
Example: reduce the likelihood of war

c: To decrease the volume and concentrate the flavor of by boiling
Example: add the wine and reduce the sauce for two minutes

In all three of these, we’re taking a group of things and consolidating, or shrinking them through some process.

In programming it’s the same. We’re taking a list of items and consolidating the list into to one new value using an accumulator.

Function Signature

Now let’s take look at the function signature for reduce.

This part is pretty straightforward, reduce takes a callback, and an initialValue to seed the accumulator. Most of the complexity here is in the function signature for the callback. Let’s dive into that.

This is a bit more intimidating, so let’s break it down.

The accumulator is thing we’re trying to reduce our list down to. It accumulates the list of items into one value. That value could be an object or even a new array, but it’s considered one value.

Whatever gets returned from the callback is passed in as the accumulator to the next iteration. When we're done, the final state of the accumulator is returned as the value.

currentValue is less scary, it’s the current item in the list we’re iterating over. This might be more familiar if we use for loop syntax.

Next is currentIndex. In the example above this would be i. We can make this a bit more explicit though.

Lastly, list. This is the entire list we’re iterating over. This can be useful if you want to access some other element in the original list. To be honest, I’ve never needed to access the original list, but it’s available.

Implementation

Now that we understand the function signature, let’s implement it from scratch.

One caveat, to avoid complexity this implementation takes the list as an argument as opposed to adding it to the prototype of array. You can see a proper polyfill at MDN [2].

As you can see, the bulk of this function is error checking. We check to make sure we have a valid list and callback. We also ensure the user has either provided an initialValue or we can derive one from the list.

In fact if we assume that the user always provides valid input this is the entire function.

This function takes a list, iterates over it, and calls a callback on the previous value along with each item in the list. it then returns the final value.

All of The Examples

Hopefully you now understand how reduce works, but it may not be clear why you would want to use it. First, let’s get the sample data out of the way. This is the same data as last time.

keyBy

Now that we have the sample data out of the way, let’s look at our first example, keyBy.

This function takes a list of objects and a key, and keys those objects by a property on them. In this example our initialValue is {}.We iterate over each item in the list, get item[key] and use it as our key in the accumulator.

As an aside, this implementation is naive and will overwrite on conflict.

Given the list of wizards above, we could call keyBy(wizards, 'name') and get the following output.

groupBy

Next up is groupBy, this function takes a list and groups the items by the output of a given function, or iteratee.

Again, we take a list, but this time we take a function, or iteratee. We call iteratee on each item in the list and use this to determine what key on the accumulator, or acc, we place the item in.

We then make sure there’s an array allocated at the key and push the item onto that array.

If we call groupBy(wizards, wizard => wizard.house), we’ll get the following output.

chunk

Another popular example, chunk. In this case we want to take a big list and split it into a list of smaller lists.

Above, we take a list and a size for our sub-lists. Our initialValue is an array with a single empty array inside of it.

We then iterate over the list and make our smaller lists. Every time one of our sub-lists reaches size, we move on to the next one.

If we call const chunkedWizards = chunk(wizards, 3), we’ll get the following value for chunkedWizards.

We now have 3 lists of 3 wizards. It’s worth noting that this worked out nicely because we had exactly 9 wizards. If we had 8 total wizards, our final list would be of size 2. If we had 10 total wizards, our final list would be of size 1.

flatten

We’ve chunked wizards into chunkedWizards, but what if we want to undo that? Well that’s what flatten is for.

In this example we take a list and iterate over it. Whenever we see an array, we concat it with the accumulator, or acc. Whenever we see anything else we push it onto the accumulator. Our initialValue for our accumulator this time is an empty array or [].

If we call flatten(chunkedWizards) we get our original list.

flatMap

We can take this a step further with flatMap. This maps over the list and flattens any results from the given function.

Hopefully by now you’re getting the hang of it. We iterate over the list and call func on each item. We then flatten the output of func.

Let’s look at a function call:

flatMap(wizards, wizard => [wizard.name, wizard.house, wizard.points])

This takes a wizard and returns all of it’s properties as items in an array. The output of this is one big array.

Map and Filter

One last fun fact, we can implement map and filter with reduce.

If you're looking for reasons to use map or filter, check out my previous article, map, filter, reduce.

Wrapping Up

Reduce is one of the most powerful functions built into JavaScript. While most of the functions above are available in lodash [3], they’re good examples of when you may want to use reduce. Give them a try and feel free to tweet questions @MatthewGerstman.


References

  1. https://www.merriam-webster.com/dictionary/reduce
  2. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
  3. http://lodash.com