Array Methods: map, filter, reduce and When a Loop Is Clearer
Use map, filter and reduce on a list of orders, plus find, some and toSorted, and know when a for...of loop reads better. After this, you can answer report questions in a few clear lines.
Course content
Sign in to save your progress.
-
Set Up Projects That Don't Break Each Other
-
Node.js and npm: Running JavaScript Outside the Browser
-
package.json and Lock Files: Same Install Every Time
-
-
-
Organize Code Other People Can Read
-
JavaScript Modules: import, export and Splitting One Big File
-
JavaScript Classes: When an Object Literal Is No Longer Enough
-
Destructuring and Spread Without the Confusion
-
-
-
Work With Real Data
-
Array Methods: map, filter, reduce and When a Loop Is Clearer
-
Working With JSON: parse, stringify and What Breaks
-
Dates in JavaScript Without Losing a Day
-
-
-
Waiting for Things
-
Callbacks to Promises: Why the Code Runs Out of Order
-
async/await and fetch: Calling a Real API
-
-
-
Prove It Works Before Someone Else Finds Out
-
try/catch and Custom Errors
-
Your First Tests With Vitest
-
-
- Final Check: JavaScript Intermediate
Skip ahead: If you already chain filter and map and know when reduce is a bad idea, skip to "When a loop is clearer".
Most real data in JavaScript is a list of objects: orders, products, users. Most of what you do with it is one of three things: change every item into something else, keep only some items, or boil the whole list down to one value. map, filter and reduce are those three things. They don't do anything a for...of loop can't. They just say what you're doing in the name, which makes code easier to read, until you overuse them.
Every example here uses this list:
const orders = [
{ id: 1, customer: "Ana", status: "paid", total: 1200 },
{ id: 2, customer: "Ben", status: "pending", total: 450 },
{ id: 3, customer: "Ana", status: "paid", total: 300 },
{ id: 4, customer: "Cris", status: "cancelled", total: 900 },
];
map: one new item for every item
const labels = orders.map(order => `#${order.id} ${order.customer}`);
console.log(labels);
[ '#1 Ana', '#2 Ben', '#3 Ana', '#4 Cris' ]
Same length in, same length out. The original array isn't changed.
filter: keep only some items
const paid = orders.filter(order => order.status === "paid");
console.log(paid.map(order => order.id));
[ 1, 3 ]
The function returns true to keep an item and false to drop it. If nothing matches, you get an empty array, not undefined.
reduce: the whole list down to one value
const revenue = orders
.filter(order => order.status === "paid")
.reduce((sum, order) => sum + order.total, 0);
console.log(revenue);
1500
reduce takes a function and a starting value. sum starts at 0, and for each order the function returns the new sum. Read the chain top to bottom: keep the paid orders, add up their totals.
⚠ Warning: Always pass the starting value to reduce. Without it, reduce uses the first item as the start. For a sum of objects that means adding numbers to an object, and on an empty array it throws TypeError: Reduce of empty array with no initial value. An empty list of orders is normal. Your report shouldn't crash on a quiet day.
The relatives you'll use just as often
find: the first item that matches, orundefined.orders.find(o => o.id === 3).some/every: true or false. "Is any order pending?" "Are all orders paid?"includes: for simple values.["paid", "pending"].includes(status).toSorted: a sorted copy.orders.toSorted((a, b) => b.total - a.total). The oldersortsorts the original array in place, which surprises people.
When a loop is clearer
reduce can do almost anything, which is the problem. Here's total per customer with reduce:
const byCustomer = orders.reduce((acc, o) => ({ ...acc, [o.customer]: (acc[o.customer] ?? 0) + o.total }), {});
It works. It also copies the whole object on every item and takes a minute to read. The loop:
const byCustomer = {};
for (const order of orders) {
byCustomer[order.customer] = (byCustomer[order.customer] ?? 0) + order.total;
}
console.log(byCustomer);
{ Ana: 1500, Ben: 450, Cris: 900 }
My rule: use map and filter freely, reduce for sums and simple totals, and a loop when you're building an object, need to stop early, or need more than one line of logic per item. Nobody gets points for fitting it in one expression.
💡 Tip: When a chain gets long, give the middle steps names: const paidOrders = orders.filter(...). You can then console.log(paidOrders) while debugging instead of taking the chain apart.
Common mistakes
- Forgetting
returnin amapcallback with braces:orders.map(o => { o.total * 2 })gives an array ofundefined. Remove the braces or addreturn. - Using
mapjust to loop, ignoring the result. Usefor...oforforEach. - Changing the objects inside
map(o.total = ...).mapgives a new array, but the objects in it are the same objects. - Missing starting value in
reduce. - Sorting the original array with
sortwhen you meant to sort a copy.
What happens if you skip this
You'll keep writing loops with counters for everything, which is fine, until you have to read other people's code, which is full of these. Or you'll learn reduce first and use it for everything, and the next person who reads your report code will rewrite it to understand it.
How would you get the names of customers who have at least one paid order, without duplicates?
About
Use map, filter and reduce on a list of orders, plus find, some and toSorted, and know when a for...of loop reads better. After this, you can answer report questions in a few clear lines.
- Duration
- 5 min
- Last update
- 08/30/2026
Comments (0)
Sign in to commentNo comments yet.