How to use .map(), .reduce(), and .filter()

A look at three different higher-order functions that make manipulating arrays much easier

Published May 17, 2022

Array methods

JavaScript has plenty of built-in methods, with several being used to manipulate arrays. For instance, the .includes() method checks whether a specified element exists within an array:


const myArray = ['cat', 'dog', 'mouse'];

console.log(myArray.includes('dog')); // true
console.log(myArray.includes('cow')); // false
        

...this is only one of the many convenient array methods JavaScript offers; a full list of them can be found on MDN Web Docs.

Remembering every single method is impossible, but there are three methods you should definitely know: .map(), .filter(), and .reduce(). These three methods are also referred to as higher-order functions. A higher-order function is a function that operates on another function, either by taking it as an argument or returning it. This article will cover all three of these higher-order functions and explain how they work; let's begin with the .map() function.


Using .map()

Here, I have an array called people, which is full of objects; each object holds a property of name and age:


const people = [
  {
    name: "John",
    age: 22,
  },
  {
    name: "Jane",
    age: 12,
  },
  {
    name: "Jack",
    age: 45,
  },
  {
    name: "Jill",
    age: 73,
  },
];
        

...next, I want to loop over this array in order to retrieve all the name values from each object, then store each value inside an empty array. Traditionally, this can be accomplished by initializing an empty array and using a for loop to grab each name value. Then, each value is pushed into the empty array by using the .push() method...


const onlyNames = [];
for (let i = 0; i < people.length; i++) {
  onlyNames.push(people[i].name);
}

console.log(onlyNames);
// ['John', 'Jane', 'Jack', 'Jill']
        

...this works perfectly fine, but it's quite a bit of code. To fix this, I'll use the .map() function instead...


const onlyNames = people.map((individual) => {
  return individual.name;
});

console.log(onlyNames);
// ['John', 'Jane', 'Jack', 'Jill']
        

...the .map() function creates a new array by transforming every element in an array individually. It takes in another function, also known as a "callback function"; the callback function takes a parameter that represents the current element being processed in the array, which in this case is individual. Finally, the name of each individual is returned and stored inside an array called onlyNames.

Despite both code snippets doing the exact same thing, the logic greatly differs. In the .map() function, the only logic being explicitly passed is return individual.name. Unlike the former example, you don't have to initialize an empty array, create a loop, or push elements into the array — the .map() function handles everything. This makes the code much more concise, improves reusability, and reduces the chances of bugs.


Using .filter()

The next higher-order function I'll cover is .filter(). This function creates a new array by removing elements that don't match the specified criteria. As an example, I'll use the same people object from the previous explanation:


const people = [
  {
    name: "John",
    age: 22,
  },
  {
    name: "Jane",
    age: 12,
  },
  {
    name: "Jack",
    age: 45,
  },
  {
    name: "Jill",
    age: 73,
  },
];
        

...next, I want to create an array that only holds the names of those with an age greater than 25. Traditionally, this can be accomplished like this...


const onlyOldPeople = [];
for (let i = 0; i < people.length; i++) {
  if (people[i].age > 25) {
    onlyOldPeople.push(people[i].name);
  }
}

console.log(onlyOldPeople);
/*
[
  {
    name: "Jack",
    age: 45,
  },
  {
    name: "Jill",
    age: 73,
  },
];
*/
        

...the if conditional statement will search for anyone older than 25 and add them to the empty array. Now, I'll accomplish the same thing with the .filter() function...


const onlyOldPeople = people.filter((individual) => {
  return individual.age > 25;
});

console.log(onlyOldPeople);
/*
[
  {
    name: "Jack",
    age: 45,
  },
  {
    name: "Jill",
    age: 73,
  },
];
*/
        

...just like with the .map() function, the .filter() function is much more concise with less logic — have a look at both code snippets side-by-side...


// Traditional way
const onlyOldPeople = [];
for (let i = 0; i < people.length; i++) {
  if (people[i].age > 25) {
    onlyOldPeople.push(people[i].name);
  }
}

// .filter() way
const onlyOldPeople = people.filter((individual) => {
  return individual.age > 25;
});
        

...furthermore, since the the .filter() function only contains a single line of code, it can be condensed even further...


// Traditional way
const onlyOldPeople = [];
for (let i = 0; i < people.length; i++) {
  if (people[i].age > 25) {
    onlyOldPeople.push(people[i].name);
  }
}

// .filter() way
const onlyOldPeople = people.filter(individual => individual.age > 25);
        

...now you're looking at 141 characters compared to 71 characters — a 50% reduction in code size! It may not seem like much, but when you're working on bigger projects, the difference becomes significant, especially with flexibility.


Using .reduce()

The next higher-order function you should be familiar with is .reduce(). This function takes all the elements inside an array and "reduces" them into a single value. For this example, I'll create an array of objects, with each object holding an amount property with different numbers:


const money = [
  {
    amount: 223,
  },
  {
    amount: 342,
  },
  {
    amount: 649,
  },
  {
    amount: 195,
  },
  {
    amount: 477,
  },
];
        

...with the data given, I want to create a function that returns the sum of each amount. Traditionally, that would be accomplished like this...


let sum = 0;
for(let i = 0; i < money.length; i++) {
  sum += money[i].amount;
}

console.log(sum);
// 1886
        

...sum is initialized with a value of 0 but increases after each iteration. Simple enough, right? Now, I'll do the exact same thing using the .reduce() function. Unlike the .map() and .filter() functions, the .reduce() function works a little differently. While it does take in a callback function like other higher-order functions, the syntax is a bit different — have a look...


array.reduce((previousValue, currentValue) => {
// ...
}, initialValue)
        

...just like .map() and .filter(), the callback function takes in the current value, except this time, it's the second argument. The first argument will hold the previous value, and it's given an initial value after the callback function. This may sound a bit confusing, but the logic is quite simple...


const finalOutput = money.reduce((sum, m) => {
 return sum += m.amount;
}, 0)

console.log(finalOutput);
// 1886
        
  1. The sum is initialized with a value of 0.
  2. sum is added with the first element, m[0].amount, which holds a value of 223.
  3. sum is now 223.
  4. sum is added with the second element, m[1].amount, which holds a value of 342.
  5. sum is now 565.
  6. This continues for every iteration until the final element is added to the sum, which will print a final value of 1886.

Applying higher-order functions

The best way to get comfortable with new concepts is to apply them. Instead of using for loops in your code, opt for a higher-order function instead — they're shorter, have higher reusability, and they're easier to work with!