Arrays are great for storing data, but what if you need something more? A total, a summary, or a new shape entirely?
Well that's where reduce()
comes in.
In this guide I’ll break down what this function is, how it works, give examples of how you can use it, as well cover common mistakes to avoid.
Sidenote: If you want to learn more about JavaScript, be sure to check out Andrei’s Complete Web Developer course, as well as my Beginner and Advanced JavaScript projects:
We have everything you need to become a full-stack JavaScript developer in 2025.
Graduates from these programs are now working at Google, Tesla, Amazon, Apple, IBM, Uber, Facebook, Shopify + other top tech companies. There’s no reason why you couldn’t do the same!
With that out of the way, let’s get into this 5-minute tutorial.
When you’re writing JavaScript, you’re usually working with groups of things, such as a list of names, numbers, or records from a form. These collections are stored in arrays, which is just another name for lists of items in a specific order - kind of like a shopping list.
For example
Here’s an array with 3 items.
const colors = ['blue', 'red', 'green'];
But the goal isn’t usually to just store a list like this. You probably want to do something with it right? Maybe add up the total of some numbers, count how many times something appears, or perhaps transform it into something else entirely.
And that’s exactly where reduce()
comes in.
At its core, reduce()
helps you take a list and turn it into a single result. That could be a number, a string, an object, or even another array. You get to decide what the final result looks like, and reduce()
helps you build it, step by step.
Here’s the basic shape of how it works:
array.reduce((accumulator, currentValue) => {
return updatedAccumulator
}, initialValue)
That might look confusing right now, but don’t worry. Once you see it in action, it starts to make more sense.
In plain terms:
0
, {}
, or []
)The key idea is that each step builds on the last, and when the loop ends, you’re left with the final result.
Now that you’ve got the gist, let’s look at some real problems you can solve using reduce()
.
This is one of the simplest and most common uses for reduce()
, which is taking a list of numbers and adding them up into a single number.
For example
Let’s say that you’ve got a shopping cart represented as an array of prices, and you want to know the total combined cost.
Here we can see all the item prices:
const prices = [12.99, 5.99, 23.50, 9.99]
So, you want to end up with:
42.48
Here’s how you’d do it with reduce()
:
const total = prices.reduce((sum, price) => {
return sum + price
}, 0)
console.log(total) // 42.48
So let’s break this down:
0
as our initial value. That’s the starting value of sum
price
is 12.99
. We add that to sum
(which is currently at 0
), and return 12.99
price
is 5.99
. Now sum
is already 12.99
, so we add the 5.99
and return 18.98
42.48
Each time, reduce()
returns the updated total, and passes it to the next step. No loop needed. No manual tracking. Just a clean way to move through the list and build your result.
This pattern is really common any time you want to organize a list into buckets based on some property.
For example
Let’s say you have a list of tasks, and each one has a status
like “open” or “done.”
const tasks = [
{ name: 'Write draft', status: 'open' },
{ name: 'Fix typos', status: 'done' },
{ name: 'Add code sample', status: 'open' },
{ name: 'Publish', status: 'done' }
]
However, you want to group them based on their status so you can see how many are in progress or completed, so that it looks something like this:
{
open: [
{ name: 'Write draft', status: 'open' },
{ name: 'Add code sample', status: 'open' }
],
done: [
{ name: 'Fix typos', status: 'done' },
{ name: 'Publish', status: 'done' }
]
}
Here’s how you’d do it with reduce()
:
const grouped = tasks.reduce((acc, task) => {
const key = task.status
if (!acc[key]) {
acc[key] = []
}
acc[key].push(task)
return acc
}, {})
So let’s break this down:
{} (acc)
status
, and use that as the keyHandy right?
Sometimes an API doesn’t give you all your data in one go. Instead, it returns pages like a list of products, messages, or search results, and each as its own array.
This means that before you can work with the full dataset, you’ll often need to combine those pages into one flat list. The good news is that’s where reduce()
comes in.
Let’s say you’re fetching user comments from three pages of an API, and each page returns an array:
const commentPages = [
['Nice post!', 'Thanks for sharing'],
['Great tips'],
['This helped a lot', 'Bookmarked']
]
Before rendering them in your UI, you want one clean list of all comments:
['Nice post!', 'Thanks for sharing', 'Great tips', 'This helped a lot', 'Bookmarked']
Here’s how you’d do that with reduce()
:
const flat = commentPages.reduce((acc, current) => {
return acc.concat(current)
}, [])
(acc)
.concat()
to merge it into the growing listThis is super common when working with paginated APIs, file uploads (one batch at a time), or form data collected in steps. Instead of loops or .push()
, reduce()
gives you a cleaner, declarative way to merge everything together.
Let’s say a user fills out a form on your site and picks a few tags to describe their interests. You want to take that array of values and turn it into a readable sentence like:
“You’re subscribed to: marketing, analytics, growth.”
Basically just letting them know the 3 confirmed topics they’ve subscribed to.
Now, that sounds simple but real-world arrays often include empty strings, null values, or fields you want to ignore.
For example
const selected = ['marketing', '', 'analytics', null, 'growth']
If you just used .join(', ')
, you’d get awkward gaps like this:
"marketing, , analytics, , growth"
But reduce() gives you the control to clean it up and format it exactly how you want.
Here’s how:
const sentence = selected.reduce((acc, word) => {
if (word && word.trim()) {
return acc.length > 0 ? acc + ', ' + word : word
}
return acc
}, '')
const final = `You're subscribed to: ${sentence}.`
Let’s unpack what’s happening here:
We start with an empty string as the initial value (acc)
Each time through the loop, we check if the word
is valid:
null
, undefined
, or, using trim()
determine that it’s not an empty/whitespace-only stringIf it is valid:
If it’s invalid, we skip it entirely
At the end, we’ve built a clean, readable list with proper punctuation
Then we wrap the result in a full sentence like:
"You're subscribed to: marketing, analytics, growth."
This approach works anywhere you need full control over how strings are built from arrays:
Once you’ve seen how to shape a string this way, .join()
starts to feel a bit limiting.
This pattern comes in handy any time you’re dealing with repeated data across multiple objects and need to combine it into a single summary. Perhaps daily sales logs, API responses from different regions, or even vote counts from different polls.
For example
Imagine you run an online grocery service with multiple warehouses. Each warehouse tracks how many units of each fruit were sold that day:
const reports = [
{ apples: 3, oranges: 5 },
{ apples: 2, bananas: 4 },
{ oranges: 1, bananas: 2 }
]
Your dashboard needs to show the total units sold per fruit, across all warehouses. (Obviously they would sell higher numbers than this, but just so you get the idea).
So you want to transform that into:
{ apples: 5, oranges: 6, bananas: 6 }
Here’s how you’d do that with reduce()
:
const totals = reports.reduce((acc, report) => {
for (let fruit in report) {
acc[fruit] = (acc[fruit] || 0) + report[fruit]
}
return acc
}, {})
So let’s break this down:
{}
as the accumulator (acc)
apples
, oranges
)This approach scales no matter how many reports you have or which fruits appear. It’s dynamic, flexible, and avoids hardcoding anything.
You’ll often use this trick any time you’re merging totals, building summaries, or syncing data from multiple sources.
Pretty cool, eh?
So now you can see how it works, let's look at some common issues when using this function.
These are the most common mistakes that I see developers making.
This happens a lot, because once people learn how powerful reduce()
is they start using it everywhere, even when it’s not the best choice.
But reduce()
is meant for one thing: taking a list and turning it into a single result. That result might be a number, an object, a string, or a new structure entirely. But if you’re not actually doing that, you probably don’t need to use it.
For example
map()
will do the job with less code and more clarityfilter()
is a better fitYou’ll know you’re misusing reduce if you find yourself adding a lot of conditions inside the callback, or doing extra work just to make it behave like a different method. If the code feels more confusing than it should, that’s usually your sign 😉.
reduce()
is brilliant when you actually need to reduce something. But when you're just transforming or selecting values, it's okay to reach for something simpler.
This one sneaks up on a lot of people, but when you use reduce()
, you have the option to pass in an initial value. Usually something like 0
, {}
, or []
, depending on what kind of result you’re building.
And while JavaScript technically lets you skip it, that can lead to subtle bugs and confusing behavior.
Here’s what happens:
If you don’t pass an initial value, reduce()
will automatically use the first item in the array as the starting value, and then begin the loop with the second item.
That means:
reduce()
just crashes with an errorNot great right?
Now compare that to explicitly setting an initial value:
So as a general rule, always pass an initial value. Even if it feels optional, it’ll save you weird bugs and make your code easier to trust.
This one can be frustrating because the code might look fine at first glance, but your result ends up being undefined
, or just completely wrong.
So here’s what’s happening:
Every time your reduce()
callback runs, it needs to return the updated version of the result you’re building. That return value becomes the input for the next step in the list.
However, if you forget to return something, or return the wrong thing, everything breaks.
Take this example:
const total = numbers.reduce((sum, num) => {
sum += num
})
It looks okay, but the total will be undefined
.
Why?
Because there's no return sum
at the end of the function, and so without that, reduce()
doesn't know what the next value of sum
should be.
Even more confusing, you might still see partial results in the middle of the loop, which makes the bug harder to spot!
The solution is fairly simple, and it’s to always double-check that your callback returns the updated result at the end of each step.
So:
{}
, you must include a return
keyword// This works
const total = numbers.reduce((sum, num) => sum + num, 0)
Small detail, but it makes all the difference.
One of the things that makes reduce()
powerful is that you can do almost anything inside that callback function. But that’s also what makes it dangerous.
It’s easy to cram a bunch of logic inside it such as if statements, calculations, nested conditions, until suddenly it becomes hard to tell what the code is even doing.
When that happens, it’s usually a sign that your function is trying to do too much.
For example
const grouped = data.reduce((acc, item) => {
if (item.status === 'open') {
if (!acc.open) acc.open = []
acc.open.push(item)
} else if (item.status === 'closed') {
if (!acc.closed) acc.closed = []
acc.closed.push(item)
}
return acc
}, {})
This code block works but it's not very easy to follow. You’ve got branching logic, repeated code, and a lot happening in one block.
In cases like this, it's usually better to move that logic into a separate function. That keeps your reduce clean, and makes the code easier to test, read, and fix later.
Just because you can do it inside reduce, doesn’t always mean you should!
So as you can see, reduce is an incredibly handy function to use. However, the best way to fully learn anything is to give it a try for yourself, so go ahead and pick a real problem you’ve run into with an array, and see how you’d solve it using reduce.
Once you do, you’ll start to see just how often this one function can simplify the way you write code.
Remember, if you want to learn more about JavaScript, be sure to check out Andrei’s Complete Web Developer course, as well as my Beginner and Advanced JavaScript projects:
We have everything you need to become a full-stack JavaScript developer in 2025, build a portfolio, and get hired.
In fact, graduates from these programs are now working at Google, Tesla, Amazon, Apple, IBM, Uber, Facebook, Shopify + other top tech companies. There’s no reason why you couldn’t do the same!
As a ZTM member, you get access to all these courses and more in a single membership.
Plus, once you join, you'll have the opportunity to ask questions in our private Discord community from me, other students and other working tech professionals, as well as access to every other course in our library!