1 Simple Trick to Boost Performance Using Reduce
Tags: webdev, javascript, tutorial, webperf
Published: 2020-06-30 05:34:52

Have you ever realised that using a **map** followed by a **filter**, or vice versa, is quite common? Did you know that you could half the computation time needed if you just used a **reduce** instead? 

We will begin by recapping the three array methods. Feel free to jump ahead to [section 5](#map-filter-reduce) if you are already comfortable with these.

### Contents
1. [Overview](#overview)
2. [Map](#map)
3. [Filter](#filter)
4. [Reduce](#reduce)
5. [Map + Filter = Reduce](#map-filter-reduce)
6. [Performance](#performance)

### Overview
Map, filter and reduce are all methods on the Array prototype. They are used for different purposes, but they all involve iterating over the elements within an array using a callback.

### Map
**Map** returns an array of the same length of the original array it was called on. It takes one parameter, a function, which can take 3 arguments:
1. The current item to iterate over
2. The index of the current item
3. The original array

Map does not mutate the original array, it creates a new array, so the value of a map must be assigned to a variable like so:

``` javascript
const nums = [10, 20, 30, 40, 50];

// assign value of map into result variable
const result = nums.map(function(item, index, arr) {});  // RIGHT

nums.map(function(item, index, arr) {}); // WRONG!
```

Let's take a look at an example where we have an array of years, and we want to get the age that these years represent, and we also want to keep the original *year* value too. This means that our array of integers, will be mapped to an array of objects, with each object having 2 properties: *year* and *age*.

**Example:**
``` javascript
const years = [1991, 1999, 2000, 2010, 2014];
const currentYear = (new Date).getFullYear();

const ages = years.map(function(year) {
  const age = currentYear - year;

  // each element will now be an object with 2 values: year & age	
  return {
    year,
    age
  }
});
```

We now have an array, *ages*, which looks like this:

``` javascript
ages = [
  {year: 1991, age: 29},
  {year: 1999, age: 21},
  {year: 2000, age: 20},
  {year: 2010, age: 10},
  {year: 2014, age: 6}
]
```

### Filter
**Filter**, as it sounds, filters the elements we want from an array into a new array, disregarding any elements we do not want.
It takes one parameter, a function, which can take 3 arguments:
1. The current item to iterate over
2. The index of the current item
3. The original array

The function acts as a [predicate](https://codepen.io/Universalist/post/predicates-in-javascript), and returns the contents of the array, as they originally were, into the newly assigned variable. Unlike map, filter does not necessarily return an array with the same length as the array it was called on.

Like map, filter does not mutate the original array, so the value of a filter must be assigned to a variable.

Let's take a look at an example where have a *years* array, which represents the year people were born, and we want a new array which contains only the years which would equate to a person being over the age of 18. 

**Example:**
``` javascript
const years = [1991, 1999, 2000, 2010, 2014];
const currentYear = (new Date).getFullYear();

const over18 = years.filter(function(year) {
  // if the year equates to over 18, then put that year
  // into our new over18 array
  return (currentYear - year) > 18;
});
```

We now have an array, *over18*, which looks like this:
``` javascript
over18 = [1991, 1999, 2000];
```

### Reduce
**Reduce** *reduces* an array down into a single value. That single value could be any JavaScript type; such as a string or a number, or even an array or object.

It takes two parameters:
1. A function which takes 4 arguments:

    a. An accumulator

    b. The current item to iterate over
    
    c. The current items index

    d. The source array

2. The initial value of our *single value* we want to return

Like map and filter, reduce does not mutate the original array, so the value of a reduce must be assigned to a variable.

**Example:**
``` javascript
const nums = [10, 20, 30, 40, 50];
const sum = nums.reduce(function(total, num) {
  total += num;

  return total;
}, 0);

console.log(sum);  // 150
```

This could also be written like this:

``` javascript

const nums = [10, 20, 30, 40, 50];

const reducer = (total, num) => total += num;
const sum = nums.reduce(reducer, 0);
```

We initialise our array, *nums*, and our *reducer* function which just adds a number to our current total value. We then initialise *sum* and call the reduce method, passing our *reducer* as the first argument, and our initial value we set our total at, which in this case is *0*. As 0 is our initial value, this will be the value of *total* during the first iteration.

### Map + Filter = Reduce
Now that we have recapped what map, filter and reduce do, and how they differ from each other, let us now understand the title of this article. 

It is quite common in programming that you may want to filter elements in an array, as well as change their contents slightly. I use the word *change* lightly, as we know that using these methods do not mutate our original array.

Remember:
* A filter retains the elements from an array that we are interested in, and assigns it to a new variable
* A map always assigns an array to a new variable of the same length as the array it was called on

So why call a filter and a map, when you can essentially get the task done in half the time using a reduce?

Using a reduce we can complete the task we need of filtering and mapping the contents of an array in one step, instead of two.

Let us look at an example where we have a *years* array, which represents the years people were born in, and we want to retain only those who are over the age of 18, as well as work out the age of the people.

**Example:**
``` javascript
const years = [1991, 1999, 2000, 2010, 2014];
const currentYear = (new Date).getFullYear();

const reducer = (accumulator, year) => {
  const age = currentYear - year;

  if (age < 18) {
    return accumulator;
  }

  accumulator.push({
    year,
    age
  });

  return accumulator;
}

const over18Ages = years.reduce(reducer, []);
```

We have now essentially combined the examples which we had in the [filter](#filter) section, and [map](#map) section, in to a [reduce](#reduce). This is the result:

``` javascript
over18Ages = [
  {year: 1991, age: 29},
  {year: 1999, age: 21},
  {year: 2000, age: 20}
]
```
Our original array, *years*, had 5 elements, if we used a map followed by a filter, we would have completed 10 iterations to get the same result that we got in 5 iterations with a reduce. However, under the hood, map, filter and reduce do slightly different things, so does it actually make a difference in performance?

### Performance
Let's see what an extreme, unrealistic, but simple example shows...
```javascript
let arr = [];

// populate array with 100,000,000 integers
for (let i = 0; i < 100000000; i++) {
  arr.push(i);
}

// calculate time taken to perform a simple map,
// of multiplying each element by 2
const mapStart = performance.now();
const mapResult = arr.map((num) => num * 2);
const mapEnd = performance.now();

// calculate time taken to perform a simple filter,
// of only returning numbers greater than 10,000
const filterStart = performance.now();
const filterResult = mapResult.filter((num) => num > 10000);
const filterEnd = performance.now();

// calculate time taken to perform a simple reduce,
// of populating an array of numbers whose initial value
// is greater than 10,000, then doubling this number
// and pushing it to our total
const reduceStart = performance.now();
const reduceResult = arr.reduce((total, num) => {
  const double = num * 2;

  if (double <= 10000) {
    return total;
  }

  total.push(double);

  return total;
}, []);
const reduceEnd = performance.now();

console.log(`map time (ms): ${mapEnd - mapStart}`);
console.log(`filter time(ms): ${filterEnd - filterStart}`);
console.log(`reduce time(ms): ${reduceEnd - reduceStart}`);

// map time (ms): 2415.8499999903142
// filter time(ms): 3142.439999995986
// reduce time(ms): 3068.4299999993527
```
I was extreme to push 100,000,000 integers into an array, but I wanted to show you the performance difference in seconds. The results show that it took reduce 3.14 seconds, using a reduce, to compute what was essentially done in 5.56 seconds using filter and map. And bear in mind this is just dealing with an array of integers. The computation time would have taken longer if dealing with strings or objects.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/grubivjhfdkrq05revit.png)


### conclusion  
When you see yourself using a *map* followed by a *filter*, or vice versa, consider using a *reduce* instead and complete the computation in half the time! As a programmer you need to weigh up the pros and cons, as what you may gain in terms of performance, you may lose in readability in your code.

*Reduce* has many use cases, this is just one.

Happy programming 😊

*<span>Header photo by <a href="https://unsplash.com/@chuttersnap?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">chuttersnap</a> on <a href="/s/photos/car?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span>*