There are still some situations where you still might need a for loop, or so you thought. Why not use recursion? Because it’s complicated? So what? Ever since JavaScript got tail recursion, you can prevent memory overflows when calling a function that calls itself over and over again (EDIT: tail-recursion was removed).

Still, recursion is a complex topic and not necessarily part of functional programming. Instead, I’m going to explain how we can use reducers to simulate recursion.

Let’s look at our old imperative friend first with that pesky for loop and switch to a functional reducer pattern as soon as possible:

food = [🍔, 🍟, 🍪]

initialValue = 🍽️ reducedValue = initialValue for (let i = 0, l = food.length; i < l; i++) {

reducedValue += food[i]

} 💩 = reducedValue

Seems pretty legit right? This process reduces the food array into something else; in this case, a reduce value of 💩. What we’re doing here is a summation. By summing our foods together along with the plate, we get 💩.

We can boil this reduction down into an eat function called a “reducer”:

eat = (combined, value) => (

combined + value

)

Reducers are functions that take 2 values and return a new value. The first value is the total or accumulator, and the second is the current item in the loop.

Arrays have a reduce method which takes a reducer and an initial state. It’s a similar concept that you might’ve seen in the popular state library Redux.

💩 = [🍔, 🍟, 🍪].reduce(eat, 🍽️)

The first value is our eat function and the second, our initial state: 🍽️.

This is a pretty simple reducer, but we can do even more such as creating new arrays or turning an array into an object, number, string, you name it!

Here’s what it looks like to create a filter method from reduce :

filterReducer = (

condition,

) => (

combined,

value,

) => (

condition(value)

? combined.concat(value)

: combined

)

If condition(value) is true , then create a new array with that value; otherwise, return the previous array. What we’ve done here is created something similar to Array.prototype.filter . It takes a condition when it’s first called, then it returns a reducer.

Let’s see it in action!:

handFilterReducer = (

filterReducer(value => (

value

.includes(✋)

))

) [🤷, 💁, 🙋] = (

[🤷, 💁, 🙋, 🙍]

.reduce(handFilterReducer)

)

handFilterReducer checks if the value includes a hand. Then we pass that into our reducer and filter out anyone that doesn’t have a hand.

First things first, there’s no initial state. When you have one, it combines the first array value with the initial state, but if you don’t pass one in, reduce combines the first array value with the second array value instead.

This method uses something called composition which we went over it in the first part. It’s extremely important in functional programming as it allows you to compose over functions to make new functions.

Because we developed filterReducer as a function that returns a function, we’re able to compose filterReducer to create handFilterReducer the same way we composed add to create addLightning in the first part.

Reducers like filterReducer are useful for state management. If your value doesn’t do anything with this reducer, then the reducer returns the previous state.

Let’s look at an example of state management:

countReducer = (

total,

value,

) => (

value

? total + 1

: total

) 4️⃣ = (

[🤷, 💁, 🙋, 🙍]

.reduce(countReducer, 0)

)

Now our total is a sum of the number of array items. Pretty useless right?

Let’s look at a really real example!:

partitionReducer = (

getPartitionName,

) => (

paritions,

value,

) => ({

...partitions

[getPartitionName(value)]: {

...partitions[getPartitionName(value)]

value,

}

})

As complicated as it looks, this is a reducer to partition or split our state into multiple other states. It takes an initial function just like our filterReducer ; in this case, it is a function that gets the name we’re going to use for partitioning our state.

To sum it up, partitionReducer takes our state and creates a new one. Our state is made up of an object of arrays and partitionReducer can maintain as many arrays as we give it.

This is what it looks like in use:

bunchPartitionReducer = (

partitionReducer(value => (

value

.isBunch

? 'multiple'

: 'single'

))

) initialState = {

single: [],

multiple: [],

} {

single: [🍈, 🍋],

multiple: [🍇, 🍒],

} = (

[🍇, 🍈, 🍒, 🍋]

.reduce(

bunchPartitionReducer,

initialState,

)

)

Notice the composition again. We composed partitionReducer to create bunchPartitionReducer . We initialize our reducer with initialState , and pass it an array of fruits. Some of those fruits come in bunches such as 🍇 and 🍒 while the other two are singles: 🍈 and 🍋.

We started out with an object containing two arrays, and we ended up with an object containing 2 arrays of items we separated by value.isBunch .

If you’re lost at this point, don’t worry, it only gets worse from here; actually no, we’re done. This was something to give you a quick overview of reducers. They’re incredibly powerful and allow for a lot of functional concepts such as pipeling that we went over in part 3 and part 4.

Of all the functional array methods we’ve talked about, they can all be written using reduce . This series has gone over the basics, but reduce is the most-powerful of them all. That’s why the examples are so crazy.

The transducer pattern, another form of functional pipelining, specifically relies on reducers. In fact, it takes a reducer and returns a reducer. We’ve actually created 2 functions already that are very similar to a transducer: filterReducer and partitionReducer . These composable functions allow for complex functional pipelining which you’ll see in libraries like RxJS. Just look through RxJS’s operators list, and you’ll see what I mean.

If you could only choose one tool in your toolbox for functional programming, pick the reducer. you can create all the other tools as necessary.

Recursion

As you’ve probably noticed, our reduce method is similar to a recursive function in that it relies on the previous value to calculate the next value. Since you don’t have to manage the recursion yourself, reducers are a much easier way to reason about recursion. You’ve understood it this whole time without knowing!

FEEL THE RECURSION!