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,
Parameter | Description |
initialValue | The value of the accumulator in the first invocation of the function (1st argument). Hence the name initialValue. |
accumulator | This 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. |
value | The value of the array element. |
index | The index of the array element. (Optional) |
array | The 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,
Invocation | accumulator | value | return |
First | 0 (initialValue) | arr[0] = 1 | 0 + 1 = 1 |
Second | 1 | arr[1] = 2 | 1 + 2 = 3 |
Third | 3 | arr[2] = 3 | 3 + 3 = 6 |
Fourth | 6 | arr[3] = 4 | 6 + 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.
Invocation | accumulator | value | return |
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
.
Invocation | accumulator | value | return |
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.