.reduce()

Swiss Army knife for working with arrays. Replaces all other methods (don't try this at home).

Time to read: 7 min

Briefly

The reduce() array method allows you to transform an array into any other value using a provided callback function and an initial value. The callback function will be called for each element in the array and must always return a result.

Example

Finding the sum of the elements:

        
          
          const nums = [1, 2, 3, 4, 5, 6, 7, 8]const sum = nums.reduce(function (currentSum, currentNumber) {  return currentSum + currentNumber}, 0)// 36
          const nums = [1, 2, 3, 4, 5, 6, 7, 8]

const sum = nums.reduce(function (currentSum, currentNumber) {
  return currentSum + currentNumber
}, 0)
// 36

        
        
          
        
      

Creating a new object with ID and user name:

        
          
          const users = [  { id: "1", name: "John" },  { id: "2", name: "Anna" },  { id: "3", name: "Kate" },]const usernamesById = users.reduce(function (result, user) {  return {    ...result,    [user.id]: user.name,  }}, {})// { '1': 'John', '2': 'Anna', '3': 'Kate' }
          const users = [
  { id: "1", name: "John" },
  { id: "2", name: "Anna" },
  { id: "3", name: "Kate" },
]

const usernamesById = users.reduce(function (result, user) {
  return {
    ...result,
    [user.id]: user.name,
  }
}, {})
// { '1': 'John', '2': 'Anna', '3': 'Kate' }

        
        
          
        
      

Interactive example:

Open demo in the new window

How it works

The reduce() method takes two parameters: a callback function and an initial value for the accumulator.

The callback function can take four parameters:

  • acc — the current value of the accumulator;
  • item — the current element of the array in the current iteration;
  • index — the index of the current element;
  • arr — the array itself that we are iterating over.
        
          
          const nums = [1, 2, 3, 4, 5, 6, 7, 8]// Don't forget that the accumulator comes first!function findAverage(acc, item, index, arr) {  const sum = acc + item  // If we are at the last element  // calculating the average by dividing by the number of elements:  if (index === arr.length - 1) {    return sum / arr.length  }  return sum}const average = nums.reduce(findAverage, 0)// 4.5
          const nums = [1, 2, 3, 4, 5, 6, 7, 8]

// Don't forget that the accumulator comes first!
function findAverage(acc, item, index, arr) {
  const sum = acc + item

  // If we are at the last element
  // calculating the average by dividing by the number of elements:
  if (index === arr.length - 1) {
    return sum / arr.length
  }

  return sum
}

const average = nums.reduce(findAverage, 0)
// 4.5

        
        
          
        
      

The function must return a value since in each subsequent iteration the value in acc will be the result that was returned in the previous step. A logical question that may arise here is — what value does acc take during the first iteration? It will be that initial value passed as the second argument to the reduce() method.

An initial value for the accumulator can be omitted. In this case, at the first iteration, the accumulator will be equal to the value of the first element in the array:

        
          
          const arr = [1, 2, 3]const sum = arr.reduce(function (acc, val) {  return acc + val})console.log(sum)// 6
          const arr = [1, 2, 3]
const sum = arr.reduce(function (acc, val) {
  return acc + val
})
console.log(sum)
// 6

        
        
          
        
      

In the fragment above, acc in the first iteration is equal to 1, and val is 2. Then, the accumulated value 3 is added to 3, and the result is returned.

There is an edge case in this approach. If the array turns out to be empty and the initial value is not specified, JavaScript will throw an error TypeError: Reduce of empty array with no initial value. This case needs to be handled separately, for example, by wrapping reduce() in try...catch, but it’s better to always specify an initial value.

Understanding

The use of reduce() is similar to the methods forEach, map and filter — they also take a callback function. However, reduce() has an additional argument — the current accumulated value. It can also be noticed that the order of the arguments is slightly changed.

The main feature of reduce() that is important to remember is the presence of the accumulator. The accumulator is the newly computed value. While executing the callback function, it is essential to return its value since it goes into the next iteration, where it will be used for further calculations. We can think of the accumulator as a variable whose value can change in each new iteration. With the second argument in reduce(), this variable gets its initial value.

The reduce() method is extremely useful when we want to calculate a new value by manipulating the values of an array. This operation is called aggregation. It is a powerful tool for data processing: for example, it can be used to find the sum of amounts in an array or group into other types of data.

Task: calculate the total amount of money in all accounts.

        
          
          const bankAccounts = [  { id: "123", amount: 19 },  { id: "345", amount: 33 },  { id: "567", amount: 4 },  { id: "789", amount: 20 },]const totalAmount = bankAccounts.reduce(  // The argument sum is the accumulator,  // in it we store the intermediate value  function (sum, currentAccount) {    // Each iteration takes the current value    // and adds it to the amount of money    // in the current account    return sum + currentAccount.amount  },  0 // Initial value of the accumulator)console.log(totalAmount)// 76
          const bankAccounts = [
  { id: "123", amount: 19 },
  { id: "345", amount: 33 },
  { id: "567", amount: 4 },
  { id: "789", amount: 20 },
]

const totalAmount = bankAccounts.reduce(
  // The argument sum is the accumulator,
  // in it we store the intermediate value
  function (sum, currentAccount) {
    // Each iteration takes the current value
    // and adds it to the amount of money
    // in the current account
    return sum + currentAccount.amount
  },
  0 // Initial value of the accumulator
)

console.log(totalAmount)
// 76

        
        
          
        
      

To understand how it works, you can look at the code that does the same thing but without reduce():

        
          
          const bankAccounts = [  { id: "123", amount: 19 },  { id: "345", amount: 33 },  { id: "567", amount: 4 },  { id: "789", amount: 20 },]
          const bankAccounts = [
  { id: "123", amount: 19 },
  { id: "345", amount: 33 },
  { id: "567", amount: 4 },
  { id: "789", amount: 20 },
]

        
        
          
        
      

Defining where to store the sum, which in our case is the accumulator. Here we also define the initial value of the accumulator:

        
          
          let totalAmount = 0for (let i = 0; i < bankAccounts.length; i++) {  const currentAccount = bankAccounts[i]  // In each iteration, we add  // the amount of money in the account to the current total  totalAmount += currentAccount.amount}console.log(totalAmount)// 76
          let totalAmount = 0

for (let i = 0; i < bankAccounts.length; i++) {
  const currentAccount = bankAccounts[i]

  // In each iteration, we add
  // the amount of money in the account to the current total
  totalAmount += currentAccount.amount
}

console.log(totalAmount)
// 76

        
        
          
        
      

In both examples, we have an accumulator that holds the current value and a new value is added. Only reduce() allows us to do this in one place and in a more understandable declarative style.

Tips

💡 The key to successfully using reduce() is to carefully monitor the order of the arguments and not forget to return the value.

In practice

Advice 1

🛠 reduce() is indeed often used to perform a mathematical operation on all elements of an array and produce some result.

🛠 If you want to apply several operations filter and map sequentially, you can combine them into a single function using reduce(). Sometimes this can be necessary for performance reasons, as this way there will be only one pass over the array instead of several depending on the number of methods being called. However, it is worth remembering that this approach may not always be easy to read.

Task: select even numbers, calculate their squares, and filter out numbers greater than 50.

        
          
          const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]function filterEven(num) {  return num % 2 === 0}function square(num) {  return num * num}function filterGreaterThanFifty(num) {  return num > 50}
          const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

function filterEven(num) {
  return num % 2 === 0
}

function square(num) {
  return num * num
}

function filterGreaterThanFifty(num) {
  return num > 50
}

        
        
          
        
      

Applying several methods:

        
          
          const result = numbers  .filter(filterEven)  .map(square)  .filter(filterGreaterThanFifty)console.log(result)// [64, 100]
          const result = numbers
  .filter(filterEven)
  .map(square)
  .filter(filterGreaterThanFifty)

console.log(result)
// [64, 100]

        
        
          
        
      

Through a single reduce():

        
          
          const result = numbers.reduce(function (res, num) {  if (filterEven(num)) {    const squared = square(num)    if (filterGreaterThanFifty(squared)) {      res.push(squared)    }  }  return res}, [])console.log(result)// [64, 100]
          const result = numbers.reduce(function (res, num) {
  if (filterEven(num)) {
    const squared = square(num)

    if (filterGreaterThanFifty(squared)) {
      res.push(squared)
    }
  }

  return res
}, [])

console.log(result)
// [64, 100]

        
        
          
        
      

🛠 It is often seen that reduce() is used to normalize values. For example, to transform an array of user data into an object where the key is the user ID and the value is the original object. This way you can quickly get the user object value by id, by accessing the key in the object instead of searching through the array:

        
          
          const users = [  { id: "123", name: "Vasiliy", age: 18 },  { id: "345", name: "Anna", age: 22 },  { id: "567", name: "Igor", age: 20 },  { id: "789", name: "Irina", age: 24 },]const usersById = users.reduce(function (result, user) {  result[user.id] = {    name: user.name,    age: user.age,  }  return result}, {})console.log(usersById["567"]);// { name: 'Igor', age: 20 }
          const users = [
  { id: "123", name: "Vasiliy", age: 18 },
  { id: "345", name: "Anna", age: 22 },
  { id: "567", name: "Igor", age: 20 },
  { id: "789", name: "Irina", age: 24 },
]

const usersById = users.reduce(function (result, user) {
  result[user.id] = {
    name: user.name,
    age: user.age,
  }

  return result
}, {})

console.log(usersById["567"]);
// { name: 'Igor', age: 20 }