Higher-order array methods with simple examples: map, filter, sort and reduce

Reading Time: 7 minutes 🕑

Last updated: September 27, 2022.

The use of map, filter sort and reduce usually qualifies as an advanced topic in most JavaScript textbooks.

The reason: these all belong to a class of array methods known as higher-order array methods (or sometimes ‘advanced array methods) that accept a custom function as an argument.

On the one hand, the custom function argument makes higher-order array methods more complicated to implement than simple array methods that perform a fixed function. For example, calling .push() and .pop() on an array extend or shorten it without the need for how this occurs to be specified.

On the other, this makes higher-order array methods more flexible: according to the rules of a certain method, it is possible to specify its functionality. For example, when using .filter(), it is possible to specify in the function passed in an as argument on what basis elements from the original array will be kept or discarded.

Thus, though the inclusion of a custom function in higher-order array methods requires a little more effort on the part of the programmer, this is actually a strength.

Three features of higher-order array methods

Higher-order array methods share three commons features:

  1. They accept a custom function as an argument;
  2. The custom function should provide a return value (type of value varies by method);
  3. They produce a new array (the array to which they are applied remains unaltered).

Below is an overview of the features of the four array methods we will cover in this tutorial:

Array method:Produces:Expected custom function return value:
array.map()An modified array of the same lengthmodified array element
array.filter()A subset of the arraytrue or false
array.sort()Re-orders the arraypositive or negative number
array.reduce()Reduces the entire array to a single valueaccumulated result

Let’s get started!

.map()

.map() produces a new array of the same length as the one to which it is applied. It does this by running the custom function we pass into it as many times as the original array length.

Within the custom function we pass in, we have available to us as a parameter each element in the original array. The content of the new array produced by .map() is the return value each time the custom function is run.

This makes map ideal for modifying the value of each array element in the same way.

To see how .map() works in practice, below is a first example in which the custom function we pass in returns no value:

const myArray = [3,7,10,4,3,8,2,1,5,8];

const myNewArray = myArray.map(function (element) {
  element;
});

console.log(myNewArray);
// Output: [undefined,undefined,undefined...]

In this case, .map() returns an array of undefined.

Let’s run this example again, but this time return the value of the parameter available to us in the custom function:

const myArray = [3,7,10,4,3,8,2,1,5,8];

const myNewArray = myArray.map(function (element) {
  return element;
});

console.log(myNewArray);
// Output: [3,7,10,4,3,8,2,1,5,8]

Now .map() outputs an array containing the same elements as the original. This is because the value of the parameter takes on the value of each element of the original array each time it is run. We end up with a copy of the original array because we are simply returning this value.

A copy isn’t usually very useful, though.

Returning a modified array

Let’s produced a new, modified array:

const myArray = [3,7,10,4,3,8,2,1,5,8];

const myNewArray = myArray.map(function (element) {
  return element+1;
});

console.log(myNewArray);
// Output: [4,8,11,5,4,9,3,2,6,9]

We now have a new version of the original array with a fixed change to each element (+1). This is a typical use case for .map().

If the need arises, we can make the change more complex by adding a conditional:

const myArray = [3,7,10,4,3,8,2,1,5,8];

const myNewArray = myArray.map(function (element) {
  if (element >= 5) {
    return "pass";
  } else {
    return "fail";
  }
});

console.log(myNewArray);
// Output: ['fail','pass','pass','fail','fail','pass','fail','fail','pass','pass']

Templating with .map()

In addition to modifying each element in an array in a uniform way, map is also useful for templating.

The easiest way is to wrap the content in HTML markup using a template literal. This will produce an array of templated elements:

const myArray = [3,7,10,4,3,8,2,1,5,8];

const myNewArray = myArray.map(function (element) {return `
  <div>
    <article>
       <p>Content of element: ${element}</p>
    </article>
  </div>`
});

console.log(myNewArray[0]);
//  <div>
//    <article>
//       <p>Content of element: 3</p>
//    </article>
//  </div>
Full parameters reference for array.map()

In the examples above, we have used the first and most commonly used parameter available in the custom function when using .map(): element. But there are more parameters available to help you create a return value:

array.map(function(element, index, arr) { // function statement }, this)

  • element: Current element value
  • index: Current element index
  • arr: The array to which map is applied
  • this: Value for keyword this if used in the function

An example using all parameters:

const myArray = [3, 7, 10, 4, 3, 8, 2, 1, 5, 8];

myArray.map(function(element, index, arr) { 
  return [element, index, arr, this]; 
}, "this");
// Output for first element: [3,0,[3,7,10,4,3,8,2,1,5,8],"this"]

.filter()

The filter method has similar functionality to map in that it has available to it each element in the original array as a parameter in its custom function.

The difference is that .filter expects a true or false return value from the custom function. If true, the element from the original array to which the custom function is being applied is included in the new array. If false, it is omitted. As such, filter is used for produced a subset of the original array to which it is applied.

As a first example, let's simple return true each time inside the custom function:

const myArray = [3,7,10,4,3,8,2,1,5,8];

const myNewArray = myArray.filter(function(element) {
  return true;
});

console.log(myNewArray);
// [3,7,10,4,3,8,2,1,5,8]

Every value is included in the new array. So this produces a copy of the myArray.

Now, let's return false instead:

const myArray = [3,7,10,4,3,8,2,1,5,8];

const myNewArray = myArray.filter(function(element) {
  return false;
});

console.log(myNewArray);
// []

This produces an empty array since the return value was false every time.

Now, let's use filter in the way it is intended to be used: to return true of false conditional upon a statement about each element in the array. For this, we use the element parameter:

const myArray = [3,7,10,4,3,8,2,1,5,8];

const myNewArray = myArray.filter(function(element) {
  return element < 5;
});

console.log(myNewArray);
// [3,4,3,2,1]

Now myNewArray is a subset of myArray with only values less than 5.

Full parameters reference for array.filter()

As with .map, filter also has parameters other than element that can be utilized to create its return value:

array.map(function(element, index, arr) { // function statement }, this)

  • element: Current element value
  • index: Current element index
  • arr: The array to which filter is applied
  • this: Value for keyword this if used in the function

An example using all parameters:

const myArray = [3,7,10,4,3,8,2,1,5,8];

myArray.filter(function(element, index, arr) { 
  console.log([element, index, arr, this]); 
}, "this");

// Output for first application: [3,0,[3,7,10,4,3,8,2,1,5,8],"this"]

.sort()

As its name suggests, .sort reproduces the array to which it is applied in a new order. Using it requires a little more work in the custom function than the map or filter method.

The important thing to understand to be able to write the custom function is that each time it is applied, the custom function within sort makes pairwise comparisons between the original array elements. For this reason, two parameters are necessary in the custom function (conventionally a and b).

The return value expected for each pairwise comparison is a negative of position number.

If a positive value is returned, the index value (the first parameter (a) in a pairwise comparison) in placed after the value is it being compared to (b). If negative, a is placed before b. By making a pairwise comparison between all pairs of values in the original array, the sort method reproduces the original array in a new order.

Let's do the calculation manually for a simple example to see what is actually going on here:

const myArray = [3,7,10];

const myNewArray = myArray.sort(function (a,b) {
  return a-b;
});

// First application:
// 3 - 7 = -4
// 3 placed before 7: [3, 7]

// Second application:
// 3 - 10 = -7
// 3 placed before 10 : [3, 10, 7]

// Third application:
// 7 - 10 = -3
// 7 placed before 10 [3, 7, 10]

console.log(myNewArray);
// Output: [3, 7, 10]

So the index value is initially 3 and this is compared with the next value of 7 and then 10. Because this is now the end of the array, 7 now becomes the index value and is compared with 10. And that completes all pairwise comparisons for myArray.

Because we specified a-b in the custom function, the return value is positive if b is greater than a. Because negative values are always produced in this example, the index value is never pushed forward and we end up reproduced the same array.

If we instead write b-a in the custom function, a positive value will be return every time so that the index value will be pushed each time ahead to the value to which it is being compared.

Here again is a simple example with the process explained manually in the comments

const myArray = [3,7,10];

const myNewArray = myArray.sort(function (a, b) {
  return b-a;
});

// First application:
// 7 - 3 = 4
// 3 placed after 7: [7, 3]

// Second application:
// 10 - 3 = 7
// 3 placed after 10: [7, 10, 3]

// Third application:
// 7 - 10 = 3
// 7 placed after 10 [10, 7, 3]

console.log(myNewArray);
// Output: [10, 7, 3]
Full parameters reference for array.sort()

array.sort(function(a,b) { // function statement })

  • a: Index value
  • b: Comparison value

.reduce()

The reduce method is used to produce a new array that represents the original array in a single value. This can be useful, for example, when wanting to find the sum of all values in an array.

The reduce method is run as many times as there are elements in the original array.

The custom function has two parameters available: accumulator and currentValue.

currentValue is the value of each element of the original array and accumulator is the return value from the previous application of the custom function. This enables the 'passing down' of a value in each application of the custom function.

It is also possible to include a second argument that specifies the starting value of the accumulator. If this omitted, it will be 0 by default.

The value returned by applying .reduce to an array is the value of the accumulator as the end of the cycle.

So to get the sum of all elements in the array, we can return the value of the accumulator (specified as 0 in this example) plus the value of the current element:

const myArray = [3,7,10,4,3,8,2,1,5,8];

const myNewArray = myArray.reduce(function (accumulator, currentValue) {
  console.log(accumulator); // 0,3,10,20,24,27,35,37,38,43,51
  return accumulator + currentValue;
},0);

console.log(myNewArray);
// Output: [51];

The starting value of the accumulator can be changed by providing a different value as the second argument:

const myArray = [3,7,10,4,3,8,2,1,5,8];

const myNewArray = myArray.reduce(function (accumulator, currentValue) {
  return accumulator + currentValue;
},10);

console.log(myNewArray);
// Output: [61];

By dividing this result by the length of the original array, .reduce can be used to calculate the average value of array elements

const myArray = [3,7,10,4,3,8,2,1,5,8];

const myNewArray = myArray.reduce(function (accumulator, currentValue) {
  return accumulator + currentValue;
},0);

const average = myNewArray/(myArray.length);

console.log(average);
// Output: [5.1];
Full parameters reference for array.reduce()

There are more parameters available to create each return value:

array.reduce(function(accumulator, currentValue, index, arr) { // function statement }, initialValue)

  • accumulator: Current value of passed down return values
  • currentValue: Current element value
  • index: Current element index
  • arr: The array to which filter is applied
  • initialValue: Starting value of the accumulator

An example using all parameters:

const myArray = [3,7,10,4,3,8,2,1,5,8];

myArray.reduce(function(accumulator, currentValue, index, arr) { 
  console.log( [accumulator, currentValue, index, arr]); 
}, 0);
// Output for first application: [0,3,0,[3,7,10,4,3,8,2,1,5,8]]

Summary

Higher-order array functions are a flexible way to produce a new, modified version of the array to which they are applied.

Though the writing of a custom function argument may seem intimidating at first, this is actually the source of the power and flexibility of these methods: the exact functionality can be determined by the programmer.