Array reduce
Tags: javascript, functional
Published: 2020-02-05 10:16:21

A reducer is a function that takes a collection and for each item in the collection, returns a new state. Most commonly we can use reducers to transform an old state of something to a new state of something. That could be an array to integer, array to array, array of objects representing application state to a new array of objects with the updated application state, anything really.

In most implementations the reduce function relies on 3 key components being available. Firstly is the collection to be reduced, secondly is the reducer function to run for each item in the collection and thirdly is the initial value of the reducer. As an example, in vanilla JavaScript we could do the following:

```javascript
const numbersToAdd = [1, 2, 3];

function additionReducer(previous, current) {
  return previous + current;
}

const result = numbersToAdd.reduce(additionReducer, 0);
console.log(result); // 6
```

We `reduce` our collection passing in a reducer function which receives a `previous` and `current` value and adds the two together and finally we have the initial value of `0`. What this will do is run the reducer for each iteration of the collection and use the initial value as the initial value of `previous` and when we return the result of adding `previous` and `current`, that value will then become the value of `previous` on the next iteration until there are no more items in the collection to iterate and thus, the result is returned.

## Tests

```javascript
describe('reduce', () => {
  it('should apply the addition reducer correctly', () => {
    const collection = [1, 2, 3];
    const reducerFn = (previous, current) => previous + current;
    const actual = reduce(collection, reducerFn, 0);
    const result = 6;
    expect(actual).toStrictEqual(result);
  });

  it('should return a new array of multiplied values correctly', () => {
    const collection = [1, 2, 3];
    const reducerFn = (previous, current) => {
      previous.push(current * 2);
      return previous;
    };
    const actual = reduce(collection, reducerFn, []);
    const result = [2, 4, 6];
    expect(actual).toStrictEqual(result);
  });

  it('should reduce a collection of objects and reshape them via the reducer', () => {
    const pokemon = [{
        name: "charmander",
        type: "fire"
      },
      {
        name: "squirtle",
        type: "water"
      },
      {
        name: "bulbasaur",
        type: "grass"
      }
    ];

    function pokemonReducer(output, current) {
      output[current.name] = {
        type: current.type
      };
      return output;
    }

    const actual = reduce(pokemon, pokemonReducer, {});
    const result = {
      charmander: {
        type: 'fire'
      },
      squirtle: {
        type: 'water'
      },
      bulbasaur: {
        type: 'grass'
      }
    };
    expect(actual).toStrictEqual(result);
  });
});
```

Here we can see 3 `reduce` tests which work on similar data but produce values of differing types. That is to say that we have a simple addition reducer just as with the example given in the introduction of this article but also a more complex multiplication reducer which basically [acts like a `map` function](https://dev.to/jamesrweb/array-map-4en) would since it generates a new array of multiplied values. Lastly we see a far more complex reducer which takes a collection of objects and returns a new state representation of each object as a new collection.

## Implementation

The native JavaScript implementation of `reduce` has the following signature:

```javascript
arr.reduce(function callback(accumulator, currentValue[, index[, array]]) {
  // perform actions and return the next state
}[, initialValue]);
```

We will aim to reproduce this behaviour with the following implementation:

```javascript
/**
 * @function reduce
 * @description A function to a collections values into any other type
 * @param {Array} collection - The collection to reduce
 * @param {Function} reducerFn - The reducer function to be applied on the last and current value
 * @param {*} initialValue - The initial value to apply the reducer to
 * @returns {*} The reduced value, this will be the same type as the initialValue parameter
 */
function reduce(collection, reducerFn, initialValue) {
  let output = initialValue;
  const clone = [...collection];

  for (let index = 0; index < clone.length; index++) {
    output = reducerFn(output, clone[index], index, clone);
  }

  return output;
}
```

The `initialValue` will be the default `output` of the `reduce` function if no items in the collection exist. If items exist in the collection then for each one we will reassign `output` to the value of the `reducerFn` function. The `reducerFn` function takes the same parameters as the native JavaScript implementation since that is our goal to reproduce. These parameters are the `accumulator`, `currentValue`, `index`, `array` in the native implementation but in our case they are `output`, `clone[index]`, `index` and `clone`.

> **Note:** If you haven't read the previous articles on Array method algorithms in this series, we are cloning the array to avoid mutations on the initial collection that is provided so that that remains intact.

Finally, once our `reducerFn` function commits actions against each element and generates a final `output` value, we exit the loop and return the `output` value.

Using our example of the native implementation near the top of this article, we could do the following to achieve the same results:

```javascript
const numbersToAdd = [1, 2, 3];

function reduce(collection, reducerFn, initialValue) {
  let output = initialValue;
  const clone = [...collection];

  for (let index = 0; index < clone.length; index++) {
    output = reducerFn(output, clone[index], index, clone);
  }

  return output;
}

function additionReducer(previous, current) {
  return previous + current;
}

const result = reduce(numbersToAdd, additionReducer, 0);
console.log(result); // 6
```

## Conclusions

Reducers can be quite a complex topic to discuss but just remember, a reducer merely reduces a collection to a single value. That value could be anything you want it to be but that is all it does. I love using reducers in my day to day work as they can make complex tasks much easier and [libraries such as Redux](https://redux.js.org/) use reducers as a core part of their functionality to do some real heavy lifting. Reducers are also useful for mundane tasks though such as our `additionReducer` example and so you can adapt them to many use cases quite easily. In saying this though you do want to scope reducers to highly specific use cases and they should adhere strictly to the Single Responsibility Principle as with any function or method implementation.