# Getting clever with Array#reduce

This is a cross-post of https://fknussel.com/getting-clever-with-array-reduce

Just wanted to list a handful of rather unusual yet interesting uses for `Array#reduce`. Most of the examples of `Array.prototype.reduce` out there involve adding numbers together, something along the lines of:

`const numbers = [1, 2, 3, 4, 5];const sum = numbers.reduce((acc, current) => {  return acc + current;}, 0);`

But there’s much more to `Array#reduce` than summing numbers in a list.

Note that this post is not JavaScript specific as these concepts apply to any language supporting reducing arrays.

### Functional pipelines (aka: function composition)

We can use `Array#reduce` to compose functions. Just to recap, here’s what composing functions means in Maths:

The same concept applies to programming: we can apply different functions to a value one after the other and get a result out of that composition.

Here’s how we can compose functions using `Array.prototype.reduce`:

`function double(val) {  return 2 * val;}`
`function triple(val) {  return 3 * val;}`
`function half(val) {  return 0.5 * val;}`
`function square(val) {  return val * val;}`
`const pipeline = [  square,  double,  triple,  half];`
`const initialValue = 5;const value = pipeline.reduce(  (accumulator, fn) => fn(accumulator),  initialValue);`

In this example, our pipeline determines the order of application for the functions because we are iterating over the `pipeline` array:

`half(triple(double(square(x))))`

which for the initial value of `5` results in:

`half(triple(double(square(5)))) = 75`

This is similar to Bash’s pipes:

Pipes let you use the output of a program as the input of another one

In this sample script, we first list all of the files in the current directory, then filter out anything that is not a json file and finally delete the selected files:

`ls | grep "\.json\$" | xargs rm -f`

We can rewrite this to use the same function composition notation as before:

`rm(grep(ls))`

### Recursively flatten arrays

We can use `Array.prototype.reduce` to recursively flatten an array:

`function flatten(arr) {  return arr.reduce((acc, current) => {    if (Array.isArray(current)) {      return acc.concat(flatten(current));    }        return acc.concat(current);  }, []);}`

Note how we boil down the argument to a new array, starting from an empty list and concatenating new values as we move forward.

This works even for really deep nested arrays like:

`flatten([1, 2, [3, 5, [6, 5, 4, [7, 8, [9]]], 1]);`

### Re-implementing lodash/get

Let’s consider this example:

`const person = {  name: 'John Doe'};`
`const address = person.address.street;`

This is obviously gonna throw a `TypeError` exception as we are trying to access a property of `undefined`, which is not an object.

We use Lodash’s `get` helper when querying an object for properties we are not sure exist. This is the docs page for `lodash/get`:

We can easily re-implement this helper using `Array#reduce`:

`function get(obj, path, defaultValue) {  return path.split('.').reduce((acc, field) => {    if (acc[field]) {      return acc[field];    }        return defaultValue;  }, obj);}`

Let’s use it:

`const person = {  details: {   name: {      first: 'John',      last: 'Doe'    },    email: 'john@doe.com',    dob: {      day: 15,      month: 'January',      year: 2017    }  }};`
`const day = get(person, 'details.dob.month.number', 'bummer');const month = get(person, 'details.dob.day');const year = get(person, 'details.dob.year');`
`console.log('dob is', `\${month} \${day}, \${year}`);console.log('age is', get(person, 'details.age', '/shrug'));`

Side note: for the first example, we might as well play safe by doing:

`const address = person.address && person.address.street;`

meaning we don’t really need `lodash/get`. But it’s just interesting to see how we’d go about reimplementing the utility library by using `reduce`.

### Optimising chained operations for better performance

It’s a common practice to chain multiple `map`, `filter` and `reduce` calls to the same array:

`const numbers = [10, 5, 11, 20, 14, 15, 2, 3];const result = numbers .filter(current => current % 2 === 0) .map(current => 3 * current);`

This is great in terms of readability as the intent of our code is super clear and easy to follow. However, bear in mind each time we chain one of these functions, not only we are creating a new array in memory on the process but we are also iterating again over the resulting list. This can have significant performance implications if we are dealing with massive collections.

For the example above, we can easily achieve the same result with a single call to `Array#reduce` by encapsulating the logic of the `filter` and `map`:

`const result = numbers.reduce((acc, current) => {  if (current % 2 === 0) {    acc.push(3 * current);  }    return acc;}, []);`

This way we are only looping over the initial array once and no extra arrays are created in the process. Again, not a huge performance gain in this case with just one `map` and one `filter`, but could be quite significant if there are more operations chained and the initial collection is bigger.

### Counting occurrences

For any list of recurring values, we can use `Array.prototype.reduce` to count how many times each value is present on our list.

`const countries = [  'AU', 'NZ', 'AU', 'UK', 'IT', 'IE', 'NZ', 'CH',  'IT', 'AU', 'NZ', 'AU', 'IT', 'IE', 'UK', 'NZ'];`
`const count = countries.reduce((acc, val) => {  if (acc[val]) {    acc[val] = acc[val] + 1;  } else {    acc[val] = 1;  }    return acc;}, {});`

In this case, we return an object in which keys are distinct countries and values are the number of occurrences for each country:

`{  AU: 4,  CH: 1,  IE: 2,  IT: 3,  NZ: 4,  UK: 2}`

Credits: I’ve seen some of these ideas on Mykola Bilokonsky’s Egghead course on `Array#reduce`.