Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 3

Aggregating Arrays with `reduce()`

Recommended resources

Hello! Welcome back.

In our last lesson, we mastered map() for transforming data and filter() for selecting data. You learned how to chain these methods together to create clean, readable data-processing pipelines, a cornerstone of modern JavaScript.

Today, we're tackling the final, and often most powerful, of the "big three" array methods: reduce(). It's used to aggregate an entire array into a single, final value. This might sound simple, but reduce() is incredibly versatile and can solve a surprisingly wide range of problems. While it can seem a bit tricky at first, we'll break it down step-by-step.

By the end of this lesson, you'll be able to use reduce() to aggregate array values into a single result, whether that result is a number, a string, or even a more complex data structure like an object.

1. The Core Mechanic of reduce()

At its heart, reduce() "reduces" an array to one value by iterating through it and applying a function that combines each element with an accumulating result.

The syntax looks like this:

array.reduce(callbackFunction, initialValue);

The callbackFunction itself takes at least two crucial arguments:

  1. accumulator: The value that gets built up over time. Think of it as the "running total."
  2. currentValue: The element from the array that is currently being processed.

To see this in action, let's watch a video that does an excellent job of visualizing how the accumulator and current value interact.

Array Reduce Explained With Examples - JavaScript Tutorial

The video 'Array Reduce Explained With Examples' by dcode provides a fantastic walkthrough of the reduce() method, starting with a simple sum. Pay close attention to how the previous value (the accumulator) changes with each iteration.

Please watch from 01:04 to 05:50. The narrator's use of console.log inside the reducer function is a great way to see exactly what's happening at each step of the process.

As the video demonstrated, the value you return from the callback function in one iteration becomes the accumulator for the next iteration.


2. The Critical initialValue

In the video, you saw a 0 passed as the second argument to reduce(). This is the initialValue. It sets the starting point for the accumulator before the first element is processed.

Providing an initialValue is a critical best practice.

  • If you provide it: The accumulator starts with initialValue, and the loop starts with the first element of the array.
  • If you omit it: The accumulator starts with the first element of the array as its value, and the loop starts with the second element.

Omitting it can lead to two major problems:

  1. If you run reduce() on an empty array without an initialValue, your code will crash with a TypeError.
  2. If your array contains objects, and you want to reduce it to a number (like a total price), omitting the initialValue will cause the accumulator to start as an object, leading to incorrect calculations (e.g., [object Object] + 50).

The takeaway is simple: Always provide an initialValue. It makes your code more predictable and robust.


3. Beyond Summing: The Power of the Accumulator

A common misconception is that reduce() is only for summing numbers. But the "single value" it returns can be anything: a number, a string, a boolean, an array, or an object. This is what makes it so powerful. The type of your initialValue determines the type of your result.

Let's explore a more advanced and highly practical use case you'll encounter when working with API data: grouping objects.

Imagine you fetch a list of products from your Laravel backend, and you want to organize them by category on the frontend.

const products = [
  { name: 'Wireless Mouse', category: 'electronics' },
  { name: 'Ergonomic Chair', category: 'furniture' },
  { name: 'Bluetooth Keyboard', category: 'electronics' },
  { name: 'LED Desk Lamp', category: 'home goods' },
  { name: 'Standing Desk', category: 'furniture' },
];

We want to transform this into an object where keys are the categories:

/* Our desired output:
{
  electronics: [ { name: 'Wireless Mouse', ... }, { name: 'Bluetooth Keyboard', ... } ],
  furniture: [ { name: 'Ergonomic Chair', ... }, { name: 'Standing Desk', ... } ],
  'home goods': [ { name: 'LED Desk Lamp', ... } ]
}
*/

Here's how you do it with reduce():

const productsByCategory = products.reduce((acc, product) => {
  // Get the category from the current product
  const category = product.category;

  // If this category doesn't exist as a key in our accumulator object yet...
  if (!acc[category]) {
    // ...create it, with its value as an empty array.
    acc[category] = [];
  }

  // Push the current product into the correct category's array.
  acc[category].push(product);

  // Return the accumulator for the next iteration.
  return acc;
}, {}); // <-- Our initialValue is an empty object!

console.log(productsByCategory);

In this example, the accumulator (which we've named acc) starts as an empty object {}. In each step, we check if the product's category exists as a key. If not, we create it. Then we push the product into the appropriate array. This pattern is incredibly useful for data transformation tasks.


4. Your Turn: Practice Exercise

Now let's apply this to a classic e-commerce scenario. You have a shopping cart represented by an array of objects. Each object contains the item's price and the quantity being purchased.

Your task is to calculate the total cost of all items in the cart.

const shoppingCart = [
  { id: 'p01', name: 'Wireless Mouse', price: 25.00, quantity: 2 },
  { id: 'p04', name: 'Ergonomic Chair', price: 350.00, quantity: 1 },
  { id: 'p05', name: 'LED Desk Lamp', price: 45.00, quantity: 3 },
];

Your Task:
Write a reduce() statement that calculates the totalCost of the items in the shoppingCart. You will need to multiply the price by the quantity for each item and add it to the accumulator.

Try to solve it yourself before looking at the solution!

Click to see the solution
const totalCost = shoppingCart.reduce((total, item) => {
  // Calculate the cost for the current item
  const itemCost = item.price * item.quantity;
  
  // Add it to the running total and return the new total
  return total + itemCost;
}, 0); // <-- Start with an initial value of 0 for the total.

console.log(totalCost);
// Expected Output: 485
// (25 * 2) + (350 * 1) + (45 * 3) = 50 + 350 + 135 = 535
// Whoops, let's re-calculate that. (25 * 2) is 50. (350 * 1) is 350. (45 * 3) is 135.
// 50 + 350 + 135 = 535. Let me correct the output.
// Expected Output: 535

Correction: The correct total is 535. My apologies for the calculation error in the hint. The logic is:

  • Iteration 1: total is 0, itemCost is 50. Returns 50.
  • Iteration 2: total is 50, itemCost is 350. Returns 400.
  • Iteration 3: total is 400, itemCost is 135. Returns 535.
    The final result is 535.

Conclusion

You've now explored reduce(), the most versatile of the core array methods.

Key Takeaways:

  • reduce() is for aggregating or "boiling down" an array to a single value.
  • The "single value" can be anything: a number (sum), an object (groupedData), or even a new array (flattenedArray).
  • The accumulator is the running result that's passed from one iteration to the next.
  • Always provide an initialValue to ensure your code is predictable and error-free.

Now that you've covered map, filter, and reduce, you have the primary tools for declarative data manipulation in JavaScript.

In our next lesson, we'll shift our focus from transforming data to finding data. We will explore methods like find(), findIndex(), and includes() to efficiently locate specific elements within an array.