Array.reduce
Tags: goingfunctional, arrayreduce, javascript
Published: 2022-03-06 08:34:36

---
title: Array.reduce
published: true
description: Love them or loath them but do you understand then? An explanation of how the Array.reduce method really works and when to use them.
series: Going functional one step at a time
tags: goingfunctional,  arrayreduce, javascript
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0id78fhwm0n54hsw8hqw.png
---
Why is the sea salty? Simple question but stay with me because I think the water cycle is a good analogy for the way ``reduce`` method really works.

_In short, water (H<sub>2</sub>O) evaporates from the surface of the oceans to form clouds. The clouds pass over land and condense until they start to precipitate as rain/snow fall. Eventually the falling water runs into rivers and starts on it way to the sea. En route the water picks up minerals including salts and carries them to the sea. As the cycle starts again the minerals are left behind and over time concentration levels build._

To see how the water cycle can help us understand the way ``reduce`` works we have to break it down into three element: 
* The minerals equate to the items in the array on which we are performing the ``reduce``.
* The water is the accumulator parameter or the reducer callback function.
* The ocean is the accumulator in its argument form, both in the initial and final values.

##So let's align this to code
__Please excuse the concentration measurements, they are probably way off, I am not a chemist.__

First we will prepare some test data. The following code generates simulated samples, two per month for a years. 
```javascript
const mineralsPerCycle = concentrationSamplesOverYear(24);
console.table(mineralsPerCycle);

function concentrationSamplesOverYear(samples) {
  const interval = (2 * Math.PI) / samples;
  const captureSample = i => 
    ((Math.random() + 7) / 8) * ((Math.cos(i * interval) + 2) / 3);
  return [...new Array(samples)].map((_, i) => captureSample(i));
}
```
The ``console.table`` will render the values before we use them. Below is an example but yours will have different values.
```
┌─────────┬─────────────────────┐
│ (index) │       Values        │
├─────────┼─────────────────────┤
│    0    │  0.89801916280756   │
│    1    │ 0.9567662790947499  │
│    2    │ 0.9325939089002321  │
│    3    │ 0.8992754278881672  │
│    4    │ 0.7532231143389726  │
│    5    │ 0.6765845269058688  │
│    6    │ 0.6187743088061717  │
│    7    │ 0.5157538308846997  │
│    8    │ 0.46555646525988514 │
│    9    │ 0.38054565223528175 │
│   10    │ 0.33107496732400704 │
│   11    │ 0.3348125096349211  │
│   12    │ 0.30271050596599436 │
│   13    │ 0.30352471441053985 │
│   14    │ 0.3696661578004031  │
│   15    │ 0.4156042590776569  │
│   16    │ 0.4608111994637522  │
│   17    │  0.53172225574472   │
│   18    │ 0.6594949154650602  │
│   19    │ 0.6714790771824638  │
│   20    │ 0.7728233018044018  │
│   21    │ 0.8208884212567936  │
│   22    │  0.924437922104001  │
│   23    │ 0.9497900622814304  │
└─────────┴─────────────────────┘
```
Next we will simulate the accumulation of minerals as implied by each fortnightly sample.
```javascript
let oceanConcentration = 0;
console.log(`
Initial concentration = ${oceanConcentration} mgs/ltr
`);

oceanConcentration = mineralsPerCycle.reduce(
  waterCycle,
  oceanConcentration);

console.log(`
Final concentration = ${oceanConcentration} mgs/ltr
`);

function waterCycle(currentConcentration, cycleConcentration) {
  return currentConcentration + cycleConcentration;
}

/* Output

Initial concentration = 0 mgs/ltr

Final concentration = 14.945932946637733 mgs/ltr

*/
```
Note in the above code how we have simplified the calling of the reduce method by firstly referencing the callback function and secondly referencing a variable for the initial value of the accumulator.

```javascript
function waterCycle(currentConcentration, cycleConcentration) {
  const newConcentration = currentConcentration + 
    cycleConcentration;
  console.log(`${cycleConcentration} + ${
    currentConcentration} = ${
    newConcentration}`);
  return newConcentration;
}
```

If we replace the waterCycle reducer for the above version we can see the concentration 'accumulate' with each sample.
```
0.89801916280756 + 0 = 0.89801916280756
0.9567662790947499 + 0.89801916280756 = 1.85478544190231
0.9325939089002321 + 1.85478544190231 = 2.787379350802542
0.8992754278881672 + 2.787379350802542 = 3.686654778690709
0.7532231143389726 + 3.686654778690709 = 4.439877893029681
0.6765845269058688 + 4.439877893029681 = 5.11646241993555
0.6187743088061717 + 5.11646241993555 = 5.735236728741722
0.5157538308846997 + 5.735236728741722 = 6.2509905596264215
0.46555646525988514 + 6.2509905596264215 = 6.716547024886307
0.38054565223528175 + 6.716547024886307 = 7.097092677121588
0.33107496732400704 + 7.097092677121588 = 7.428167644445595
0.3348125096349211 + 7.428167644445595 = 7.762980154080516
0.30271050596599436 + 7.762980154080516 = 8.06569066004651
0.30352471441053985 + 8.06569066004651 = 8.369215374457049
0.3696661578004031 + 8.369215374457049 = 8.738881532257452
0.4156042590776569 + 8.738881532257452 = 9.154485791335109
0.4608111994637522 + 9.154485791335109 = 9.61529699079886
0.53172225574472 + 9.61529699079886 = 10.14701924654358
0.6594949154650602 + 10.14701924654358 = 10.806514162008641
0.6714790771824638 + 10.806514162008641 = 11.477993239191106
0.7728233018044018 + 11.477993239191106 = 12.250816540995508
0.8208884212567936 + 12.250816540995508 = 13.071704962252301
0.924437922104001 + 13.071704962252301 = 13.996142884356303
0.9497900622814304 + 13.996142884356303 = 14.945932946637733
```

Unsurprisingly the callback function of the reduce method (parameter one) is called a reducer. However, one thing that confuses matters is that the callback is not called a reducer because it 'reduces' an array of (potentially) many items into a single value (it might not). It is called a reducer because (for each element of the array) it takes two arguments (primarily, we will expand on this point later) the accumulator and the element. It then reduces them to a single value to form the new accumulator.

On the point of how many parameters the ``Array.reduce`` method expects, it actually expects up to four:
1. The accumulator - the in-bound reduced value
2. The item - the element from the array to be reduced
3. The index of the element of the array (not often used)
4. The array being processed (not reduced), very rarely used.

We will explore the fact the output might not be a single value in the next section.

##Reduce, the root of many methods
The ``reduce`` method is capable of many operations (we will explore this later) and once mastered it is easy to find opportunities to use it but there are usually better options.

###The ``map`` method
Like ``reduce`` the ``map`` method takes a callback but in this case it is a mapping function that takes a value from the array and produces a new value, one for one. The new array that is created will be the same size as the input array.

If we use ``map`` in the following fashion,
```javascript
function celsiusToFahrenheit(degCelsius) {
   return (degCelsius * 9) / 5 + 32;
}

console.table([-40, 0, 16, 100].map(celsiusToFahrenheit));
```
a table of temperatures in Fahrenheit will be presented on the console for each of the Celsius temperatures in the input array.
This can also be written using the ``reduce`` method as follows using the same mapping function.
```javascript
console.table([-40, 0, 16, 100].reduce((acc, celsius) =>
   [...acc, celsiusToFahrenheit(celsius)], []));
```

###The ``filter`` method
We can do something similar to reproduce the ``filter`` method using a predicate function such as:
```javascript
const greaterThanFifty = (value) => value > 50;

console.table([20, 40, 60, 80, 100].filter(greaterThanFifty));
// 60, 80, 100
```
Now with the ``reduce`` method.
```javascript
console.table([20, 40, 60, 80, 100].reduce((acc, val) =>
   greaterThanFifty(val) ? [...acc, val] : acc, [])); 
```
In both examples using ``reduce`` make for a longer and slightly more complicated solution. However, ``reduce`` can combine both operations in a single pass.
```javascript
console.table(
    [-40, 0, 16, 100].reduce((acc, celsius) => {
        const fahrenheit = celsiusToFahrenheit(celsius);
        return greaterThanFifty(fahrenheit) ? 
            [...acc, fahrenheit] : acc;
    }, [])
); // [60.8, 212]
```
In fact the output of a ``reduce`` does not even have to be an array.
```javascript
console.table(
    [-40, 0, 16, 100].reduce(
        (acc, celsius) => ({ ...acc, [celsius]: 
            celsiusToFahrenheit(celsius) }),
        {}
    )
); // {'16': 60.8, '100': 212}
```
The above example will produce an object containing a mapping of the Celsius to Fahrenheit temperatures but only for those Fahrenheit temperatures greater than 50 degrees.

##Do's and Don'ts
In this final section I would like to offer some advice for using the ``reduce`` method.

###Do's
1. Use ``reduce`` when converting from an array to another data structure.
2. Consider using the ``reduce`` method when the operation is a combination of ``map``ping and ``filter``ing.

###Don'ts
1. Do not use ``reduce`` when there are better alternative methods. They well usually perform better as they are implemented within the JavaScript engine.
2. Don't be scared to at least explore using the ``reduce`` method when appropriate.

##Reduce's evil twin
The `reduce` method is not likely to be a method you use every day but knowing it exists and what is capable of adds another tool to your toolbox.

An even less used array method in reduce's (not so) evil twin `reduceRight`, which I think is fairly obvious what it does. `Reduce` processes the items in the array from left to right (in index order), `reduceRight` processes the array from right to left (in inverse index order). But `reduceRight` is not equivalent to `Array.reverse().reduce()` because the third parameter of the reducer function will decrease not increase as the method traverses the array.