Reduce the fear on reduce - Javascript.

Reduce the fear on reduce - Javascript.

The reduce method available on arrays on JavaScript is one of the most powerful feature of functional programming. It is used to combine an sequence of elements together as a binary operation. In general terms, when we reduce an array we produce a single value at the end which consists of a pure function and some important concepts of reduce workflow.

The image below correctly explains the three main pillars provided in javascript to support functional programming paradigm.

image.png

Map is used to create a new array based on certain manipulation of every item of existing array but not removing them.

Filter is used to create a new array based on certain condition of existing array.

Reduce is the master of all which includes a whole pipeline and can do map, filter and much more. But, as you see in the diagram it is used to produce a single value at the end.

Before getting hands-on with reduce, we need to understand few concepts first.

Accumulator

Accumulator is used to store the current value of our reduce pipeline. Initially, if we provide any initial value to the reduce function, accumulator equals that value if not accumulator takes the first value in the array and the iteration starts from second item. Not clear? Look at the examples below.

Let's start with one of the most basic reducing operation.

const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const num = arr.reduce((acc, el) => acc + el);
console.log(num); // 55

Here, the reduce function takes 1 as initial value ( first item in the array ) because no initial value is provided. After taking the first item as initial value, the invocation of function starts with the second item in the array.

Initially, the accumulator stores the same value as the initial value.

So, the initial value is 1, the accumulator holds the value 1, and the current element is 2.

After running the function we passed inside the callback, the returned value of our function is the new value to be set in the accumulator.

After one iteration.

So, the accumulator holds to value 3, and the current element is 3.

After second iteration.

So, the accumulator holds the value 6, and the current element in 4.

So, the accumulator is responsible for holding the returned new item after every iteration which is again passed while invoking the function for next item.

Guess, the above example makes it crystal clear now.

Now, let's try to do the same thing but providing initial value.

const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const num = arr.reduce((acc, el) => acc + el, 5);
console.log(num); //60

Here, we pass another argument to the reduce method of array 5. Now, the reduce method will take 5 as initial value. The iteration begins on first item because it's not taken as initial value.

So, the initial value is 5, the accumulator holds the value 5, and the current element is 1.

After one iteration.

So, the accumulator holds to value 6, and the current element is 2.

After second iteration.

So, the accumulator holds the value 8, and the current element in 3.

Hope you understood the process.

Now, let's understand the whole syntax.

reduce((acc, el) => { ... } )
reduce((acc, el, i) => { ... } )
reduce((acc, el, i, arr) => { ... } )

We tried the first syntax already. The first item we pass is the accumulated value which is equal to the initialValue ( if we pass ) or the first item of array initially.

The second syntax is also near-equivalent but it takes a third argument i which is equal to the current index of the array.

The third syntax passes the whole array as first argument.

Keep in mind, only the first two are required. All other are optional parameters, pass it if you need them.

Now, you've a foundational knowledge of reduce, let's try to see how it's a swiss knife of functional programming.

Converting an array of number to array of strings.

For this operation map may be suitable and if you do it via map, you can do something like this...

const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const num = arr.map(el => el.toString())
console.log(num); // ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]

This is a lot simpler approach. Let's try to approach this with reduce.

First let's think how we can solve this like a functional programmer.

The initial value for this operation should be blank array i.e. []. Now, on every reduce operation we convert the number into string and push into the array.

Pretty straight-forward right?

const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const num = arr.reduce((acc, el) => [...acc, el.toString()], []);
console.log(num);

If you're confused with the part [...acc, el.toString()], it means to spread the value of current acc array in the place of ...acc which means if acc equals to [1,2,3] then our new array will be replaced as [...acc] => [1,2,3], here the value inside of acc array is spread in-place.

After the current value held by the acc, we are pushing our current el.toString().

The flow can be explained as...

At first, acc = [] and current element = 1

At second, acc = ["1"] and current element = 2

At third, acc = ["1" , "2"] and current element = 3

Pretty straight forward right? If you're having issues with spread operator, consider this.

const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

const num = arr.reduce((acc, el) => {
 acc.push(el.toString());
 return acc;
}, []);

console.log(num);

Now, what if only even number is required.

Let's try with filter and map.

const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const num = arr
    .filter(el => el % 2 == 0)
    .map(el => el.toString())
console.log(num);

This does the job. Now, let's try implementing this with reduce.

So, the initial value will be []. Our callback function should return the accumulator if element is odd, else add the stringified-element to the accumulator and return it.

const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const num = arr.reduce((acc, el) => {
  if(el % 2 == 0) acc.push(el.toString());
 return acc;
}, []);
console.log(num);

Now, this is getting interesting, right?

Let's try to implement a reduce function which reverses an array. It's quite simple because we also have reduce right, which reduces the array from left.

const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const num = arr.reduceRight((acc, el) => {
  if(el % 2 == 0) acc.push(el.toString());
 return acc;
}, []);
console.log(num);

We can also do this though...

const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const num = arr
    .filter(el => el % 2 == 0)
    .map(el => el.toString())
    .reverse()
console.log(num);

This is just the surface of the power of reduce. You can do much more based on your needs. If you've more examples, feel free to drop those in comments.

Further Reading: developer.mozilla.org/en-US/docs/Web/JavaSc..