by Guido Schmitz

Higher Order Functions: Using Filter, Map and Reduce for More Maintainable Code

Source: Unsplash

Higher order functions can help you to step up your JavaScript game by making your code more declarative. That is, short, simple, and readable.

Knowing when and how to use these functions is essential. They make your code easier to understand and maintain. It also makes it easy to combine functions with each other. This is called composition, and I will not go in much detail here. In this article I will cover the three most used higher order functions in JavaScript. These are .filter(), .map() and .reduce().

Filter

Imagine writing a piece of code that accepts a list of people where you want to filter out the people that are equal or above the age of 18.

Our list looks like the one below:

const people = [ { name: ‘John Doe’, age: 16 }, { name: ‘Thomas Calls’, age: 19 }, { name: ‘Liam Smith’, age: 20 }, { name: ‘Jessy Pinkman’, age: 18 },];

Let’s look at an example of a first order function which select people that are above the age of 18. I’m using an arrow function that is part of the ECMAScript standard or ES6 for short. It’s just a shorter way of defining a function and allows you to skip typing function and return, as well as some parentheses, braces, and a semicolon.

const peopleAbove18 = (collection) => {  const results = [];   for (let i = 0; i < collection.length; i++) {    const person = collection[i];     if (person.age >= 18) {      results.push(person);    }  }
  return results;};

Now what if we want to select all the people who are between 18 and 20? We could create another function.

const peopleBetween18And20 = (collection) => {  const results = [];   for (let i = 0; i < collection.length; i++) {    const person = collection[i];     if (person.age >= 18 && person.age <= 20) {      results.push(person);    }  }
  return results;};

You may already recognize a lot of repeating code here. This could be abstracted into a more generalized solution. These two functions have something in common. They both iterate over a list and filter it on a given condition.

“A higher order function is a function that takes one or more functions as arguments.”Closurebridge

We can improve our previous function by using a more declarative approach, .filter().

const peopleAbove18 = (collection) => {  return collection    .filter((person) => person.age >= 18);}

That’s it! We can reduce a lot of extra code by using this higher order function. It also make our code better to read. We don’t care how things are being filtered, we just want it to filter. I will go into combining functions later in this article.

Map

Let’s take the same list of people and an array of names that tells if the person loves to drink coffee.

const coffeeLovers = [‘John Doe’, ‘Liam Smith’, ‘Jessy Pinkman’];

The imperative way will be like:

const addCoffeeLoverValue = (collection) => {  const results = [];   for (let i = 0; i < collection.length; i++) {    const person = collection[i];
    if (coffeeLovers.includes(person.name)) {      person.coffeeLover = true;    } else {      person.coffeeLover = false;    }     results.push(person);  }   return results;};

We could use .map() to make this more declarative.

const incrementAge = (collection) => {  return collection.map((person) => {    person.coffeeLover = coffeeLovers.includes(person.name);     return person;  });};

Again, .map() is a high-order function. It allows a function to be passed as an argument.

Reduce

I bet you will like this function when you know when and how to use it.
The cool thing about .reduce() is that most of the functions above can be made with it.

Let’s take a simple example first. We want to sum up all the people’s ages. Again, we’ll look how this can be done using the imperative approach. It’s basically looping through the collection and increment a variable with the age.

const sumAge = (collection) => {  let num = 0;   collection.forEach((person) => {    num += person.age;  });   return num;}

And the declarative approach using .reduce().

const sumAge = (collection) => collection.reduce((sum, person) => { return sum + person.age;}, 0);

We can even use .reduce() to create our own implementation of .map() and .filter() .

const map = (collection, fn) => {  return collection.reduce((acc, item) => {    return acc.concat(fn(item));  }, []);}
const filter = (collection, fn) => {  return collection.reduce((acc, item) => {    if (fn(item)) {      return acc.concat(item);    }     return acc;  }, []);}

This might be hard to understand at first. But what .reduce() basically does is start with a collection and a variable with an initial value. You then iterate over the collection and append (or add) the values to the variable.

Combining map, filter and reduce

Great, that these functions exist. But the good part is that they exist on the Array prototype in JavaScript. This means these functions can be used together! This makes it easy to create reusable functions and reduce the amount of code that is required to write certain functionality.

So we talked about using .filter() to filter out the people that are equal or below the age of 18. .map() to add the coffeeLover property, and .reduce() to finally create a sum of the age of everyone combined.
Lets write some code that actually combines these three steps.

const people = [ { name: ‘John Doe’, age: 16 }, { name: ‘Thomas Calls’, age: 19 }, { name: ‘Liam Smith’, age: 20 }, { name: ‘Jessy Pinkman’, age: 18 },];
const coffeeLovers = [‘John Doe’, ‘Liam Smith’, ‘Jessy Pinkman’];
const ageAbove18 = (person) => person.age >= 18;const addCoffeeLoverProperty = (person) => { person.coffeeLover = coffeeLovers.includes(person.name);  return person;}
const ageReducer = (sum, person) => { return sum + person.age;}, 0);
const coffeeLoversAbove18 = people .filter(ageAbove18) .map(addCoffeeLoverProperty);
const totalAgeOfCoffeeLoversAbove18 = coffeeLoversAbove18 .reduce(ageReducer);
const totalAge = people .reduce(ageReducer);

If you do it the imperative way, you will end up writing a lot of repeating code.

The mindset of creating functions with .map() ,.reduce() and .filter() will improve the quality of the code you’ll write. But it also adds a lot of readability. You don’t have to think about what’s going on inside a function. It’s easy to understand.

Where to go next?

Now that you’ve learned something about higher order functions, you might find that these articles below may be interesting to you.

Write safer and cleaner code by leveraging the power of “Immutability”
Immutability is one of the building blocks of functional programming. It allows you to write safer and cleaner code. I…medium.freecodecamp.comReduce (Composing Software)
Note: This is part of the “Composing Software” series on learning functional programming and compositional software…medium.comImperative vs Declarative Programming
At this point you’ve undoubtedly heard about imperative programming vs declarative programming. You might have even…medium.freecodecamp.comMaster the JavaScript Interview: What is Function Composition?
“Master the JavaScript Interview” is a series of posts designed to prepare candidates for common questions they are…medium.com

Thanks for reading! :)

If you enjoyed this article, hit that heart button below ❤. It would mean a lot to me and it helps other people see the story.

Say hello on Twitter