Master of Reduction
I have little to add on teaching you the common use cases of reduce. If you can use it to:
- total up values
- tally into an object
- flatten an array
- identify unique values
then you're in no bad place. But here's some more interesting cases you might come across one day.
Here's an implementation of reduce that averages an array of numbers. It totals up, and then on the last element of the array, it divides by the length of the array passed in.
OK, nothing too fancy, but it gives us the opportunity to see all four arguments that reduce will pass to the function given to it.
So why not this? I hear you cry in righteous consternation:
Yes, this will work fine, in this case. But it's important to remember that the array you start off with is not necessarily the same array as the fourth argument in the callback - if you're chaining methods, for example:
Of course, you could slice your divisor at the end too, but this approach goes against one of the principles of functional programming - give functions the arguments they need to do their job so they don't need to look outside. My version isn't perfect, to be fair - it would be nice to be able to extract and rename the function to be shared elsewhere, but what would I call it? It has very specific behaviour - sometimes total, sometimes divide. Not likely to be useful elsewhere.
This is great for composability and shows the strength in isolating atomic behaviour into individual functions. Reorder your initial array and what result would you get?
Unlike the previous examples, this makes use of the mythical second argument reduce can take, the initial value. You'll almost always need it (unless you're ok with
reduce using the first item from the array as its initial argument), but it doesn't have to be an empty string / array / object etc - start with what ever you want.
On that note :
Say you don't have control of the data that's coming in from a third party API or a database you didn't design. Then say you need to process that data into a format you don't control either - say, for a third party React component. Reduce is, essentially, for turning a collection into something else, and that can be any value you like. Define an initial value that looks like what you want it to look like. This is an example that turns a response from the Google Books API into data for a chart.js pie chart.
If this looks a bit like a tally to you, it essentially is. And by the same logic, if you know what you'll be tallying, you could declare it in your initial value to save yourself some key checking in the reduce:
Checking value existence
You may have found yourself in the position of wanting to check if a value exists inside a nested object, but not have any guarantee of the structural integirty of this object.
console.log(galaxy.solarSystem.planet.satellite.manInTheMoon) won't cut it, because not all planets have satellites and not all satellites have strange men staring down at you through your bedroom window so you can't sleep. So you find yourself doing a run of every increasing nested checking:
console.log(galaxy.solarSystem && galaxy.solarSystem.planet //etc...)
Clunky and chunky. Lodash has a
.get method that will take a dot-separated string and return either the existent value or undefined. But why not make our own with our newfound skills?
See if you can make a version that can parse a string representing nested arrays too!
Reduce can be a way of avoiding recursion without resorting to a for loop too. I'll let you figure out what this one does.
If you've never made an Array directly with the constructor before, give it a go. It'll make this strangely empty, 'sparsely populated' array, for which even
undefined is too corporeal. It has the length you'd expect, but as it is devoid of constituent values, it cannot be mapped, filtered, reduced etc.
.fill() is a neat way of populating it with undefineds, something which can at least be iterated over.
Any more interesting ones? Let us know @Northcoders!