Understanding reduce in JavaScript

Understanding reduce in JavaScript

Let me be honest, it took me a while to understand the Higher-Order Function - reduce. In my opinion, I think many faced this situation or facing it. So, let's get started with some theoretical explanation and we hop into some code.

What is reduce ?

  • It's a Higher-Order Function
  • It takes 2 arguments
    • first argument is a function which takes 4 arguments
    • second argument is the initial value
  • It's one of the methods of Array
  • It won't modify the original array

What does that 4 parameters and intialValue do ?

Array.reduce(function (accumulator, value, index, array) {
    return /* modified accumulator or just the accumulator */
}, initialValue)

Nice, now what do those parameters do? Okay,

ParameterDescription
initialValueThe value of the accumulator in the first invocation of the function (1st argument).
Hence the name initialValue.
accumulatorThis will be the return value of each invocation of the function.
It should be the same type of the initialValue on each and every invocation.
valueThe value of the array element.
indexThe index of the array element. (Optional)
arrayThe array itself. (Optional)

Enough Explaining. Show me the code!

// The common example of reduce method
function sum(acc, val) {
    // the optional parameters are omitted
    return acc + val
}
const arr = [1, 2, 3, 4]
const sumOfArr = arr.reduce(sum, 0) // = 10
// Here the initialValue's type is number 
// so the type of `sumOfArr` will be the same (number)

What's happening here? Let's see with this table,

Invocationaccumulatorvaluereturn
First0 (initialValue)arr[0] = 10 + 1 = 1
Second1arr[1] = 21 + 2 = 3
Third3arr[2] = 33 + 3 = 6
Fourth6arr[3] = 46 + 4 = 10

So the result would be the sum of the used array (here arr).

// The optional parameters are omitted for this ex. too
// and I'm gonna use arrow functions
const rick = ['never', 'gonna', 'give', 'you', 'up', 'never', 'gonna', 'let', 'you', 'down']
const ricksObj = rick.reduce((acc, val) => {
    acc[val] = (acc[val] + 1) || 1
    return acc
}, {})
// { never: 2, gonna: 2, give: 1, you: 2, up: 1, let: 1, down: 1 }

The ricksObj is a object as initialValue = {}. So here, what the does is super simple. It returns an object with properties of rick array elements and values as the occurrence of each element in the array. Let's create a table.

Invocationaccumulatorvaluereturn
First{} (initialValue)'never'acc['never'] is undefined.
So, acc['never'] = 1.
{ never: 1 }
Second{ never: 1 }'gonna'Same as above.
{ never: 1, gonna: 1 }

On third, fourth and fifth invocation, the array elements are unique so, they all get the value of 1 as per the expression (acc[val] + 1) || 1.

Invocationaccumulatorvaluereturn
Sixth{ never: 1, gonna: 1, give: 1, you: 1, up: 1 }'never'As the property never is no more undefined, the property's value get incremented.
{ never: 2, gonna: 1, give: 1, you: 1, up: 1 }

and so on.

What else does this method do !?

Let's use an API and use reduce with the response Array.

fetch('https://arrowverse-api.vercel.app/api/characters?page=1&limit=10')
  .then(res => res.json())
  .then(data => {
    // ----------------------------------------------------
    data = data.reduce((acc, val) => {
      if(val.imgUrl !== '') {
        acc.push({ name: val.name, imgUrl: val.imgUrl })
        return acc
      } else {
        return acc
      }
    }, [])
    console.log(data)
    // -----------------------------------------------------
  }
)

There are many properties in the objects inside the array but I need only the name of the character and the image URL, but some characters don't have the image URL, so I need to exclude them. The exact same result is acquired when you use map and filter, like this

fetch('https://arrowverse-api.vercel.app/api/characters?page=1&limit=10')
  .then(res => res.json())
  .then(data => {
    // ----------------------------------------------------
    data = data
        .map(val => ({
            name: val.name,
            imgUrl: val.imgUrl
        }))
        .filter(data => data.imgUrl !== '')
    console.log(data)
    // -----------------------------------------------------
  }
)

So both of them work. I personally like to use reduce as it's so compact and powerful. Try the blocks of code in the browser console

I hope you find this article helpful.