The JavaScript Array Methods You Actually Use: map, filter, reduce and Friends
Master map, filter, find, some, every, and reduce with real examples, plus the mutation traps and modern ES2023 methods that keep your code clean.
Every JavaScript codebase you will ever touch is full of arrays: products in a cart, users in a list, messages in a chat. And almost everything you do with those arrays comes down to a handful of built-in methods. Learn six of them well (map, filter, find, some, every, and reduce) and you can express most everyday logic in a line or two of readable code.
This guide walks through each one with realistic examples, shows you how to chain them, warns you about the mutation trap that bites almost every beginner, and ends with a cheat sheet worth keeping open in a tab.
Why array methods beat manual loops
You can do everything in this article with a for loop. So why bother?
Because array methods say what you are doing, not how. A for loop makes the reader track an index variable, a loop condition, and a result array just to figure out your intent. filter states the intent in its name.
Compare the two versions of the same task, "names of items under $50":
const cart = [
{ name: "Keyboard", price: 89, quantity: 1 },
{ name: "Mouse", price: 35, quantity: 2 },
{ name: "USB-C cable", price: 12, quantity: 3 },
];
// The loop version
const names = [];
for (let i = 0; i < cart.length; i++) {
if (cart[i].price < 50) {
names.push(cart[i].name);
}
}
// The method version
const cheapNames = cart
.filter((item) => item.price < 50)
.map((item) => item.name);
The method version has no index to get wrong, no off-by-one risk, and no temporary array you mutate as you go. It also returns a new array instead of changing an existing one, which matters a lot once your data flows through a UI framework like React.
Loops still have their place (we will get to that at the end), but for transforming and querying data, methods win on clarity almost every time.
The six methods you will use every day
All six take a callback function that runs once per element. The differences are in what they return.
map: transform every item
map returns a new array of the same length, where each element is whatever your callback returned. Use it when you want to convert one shape of data into another.
const prices = cart.map((item) => item.price * item.quantity);
// [89, 70, 36]
const labels = cart.map((item) => `${item.name} x${item.quantity}`);
// ["Keyboard x1", "Mouse x2", "USB-C cable x3"]
The mental model: map is a conveyor belt. Items go in one end, transformed items come out the other, and the belt never skips or drops anything.
filter: keep only what passes a test
filter returns a new array containing only the elements for which your callback returned a truthy value. The callback is a yes/no question asked of every element.
const users = [
{ name: "Amira", active: true, plan: "pro" },
{ name: "Ben", active: false, plan: "free" },
{ name: "Chen", active: true, plan: "free" },
];
const activeUsers = users.filter((user) => user.active);
// Amira and Chen
const freeAndActive = users.filter((user) => user.active && user.plan === "free");
// just Chen
If nothing passes, you get an empty array, never an error. That makes filter safe to chain into further operations.
find: get the first match
filter gives you all matches. find gives you the first one and stops looking, which is exactly what you want for lookups by ID or email.
const ben = users.find((user) => user.name === "Ben");
// { name: "Ben", active: false, plan: "free" }
const missing = users.find((user) => user.name === "Dana");
// undefined
That undefined is the classic gotcha: always handle the not-found case before you touch a property, or you will meet Cannot read properties of undefined in production.
ES2023 also added findLast, which searches from the end. It is handy for "most recent entry that matches" questions on chronologically ordered data.
some and every: ask yes/no questions
Both return a boolean instead of an array. some asks "does at least one element pass?" and every asks "do they all pass?"
const hasInactiveUsers = users.some((user) => !user.active); // true
const allOnFreePlan = users.every((user) => user.plan === "free"); // false
// Practical use: can this cart be checked out?
const cartIsValid = cart.every((item) => item.quantity > 0);
Both short-circuit: some stops at the first match, every stops at the first failure. Reach for them whenever you catch yourself writing filter(...).length > 0, which does the same job with more work and less clarity.
reduce: boil an array down to one value
reduce is the most powerful and the most abused method on this list. It walks the array while carrying an accumulator, and whatever your callback returns becomes the accumulator for the next element.
The canonical example is a cart total:
const total = cart.reduce(
(sum, item) => sum + item.price * item.quantity,
0 // initial value of the accumulator
);
// 195
It can also build objects, like grouping users by plan:
const byPlan = users.reduce((groups, user) => {
(groups[user.plan] ??= []).push(user);
return groups;
}, {});
// { pro: [Amira], free: [Ben, Chen] }
Rule of thumb: use reduce when the result is a different shape than an array, like a number, a string, or an object. If the result is just another array, map or filter will read better.
Chaining: small steps, readable pipelines
Because map and filter return new arrays, you can chain them into a pipeline where each step does one small thing:
const receiptLine = cart
.filter((item) => item.quantity > 0)
.map((item) => ({ ...item, subtotal: item.price * item.quantity }))
.filter((item) => item.subtotal >= 20)
.map((item) => `${item.name}: $${item.subtotal}`)
.join("\n");
Read it top to bottom like a recipe: drop empty lines, compute subtotals, keep the ones worth listing, format, join. Each step is trivially testable on its own.
One caution: every step in a chain walks the whole array. For a cart of ten items that is irrelevant. For a million rows it might matter, and a single loop (or a single reduce) could be the right call. Measure before you optimize; readable code that runs in two milliseconds does not need saving.
The mutation trap
Here is the mistake that costs beginners the most debugging hours. Some array methods mutate the original array in place, and three of the most common ones look innocent:
push,pop,shift,unshiftadd or remove elements in placespliceinserts or deletes in placesortandreversereorder the original array, then return it
That last one is brutal, because sort also returns the array, so it looks like it gave you a copy:
const prices = [89, 35, 12];
const sorted = prices.sort((a, b) => a - b);
console.log(sorted); // [12, 35, 89]
console.log(prices); // [12, 35, 89] <-- also changed!
If prices came from a prop, a cache, or shared state, you just silently corrupted it. Frameworks that detect changes by comparing references (React, for one) may not even re-render, because it is still the same array object.
ES2023 fixed this properly with non-mutating twins: toSorted, toReversed, and toSpliced, plus with(index, value) for replacing a single element.
const sortedCopy = prices.toSorted((a, b) => a - b);
// prices is untouched
const updated = prices.with(0, 99);
// new array with index 0 replaced
These are supported in all modern browsers and Node 20+. If you target older environments, spread into a copy first:
const sortedCopy = [...prices].sort((a, b) => a - b);
Either way, the habit to build is: treat arrays you did not create as read-only, and produce new arrays instead of editing old ones.
Cheat sheet
| Method | Returns | Mutates? | Use when |
|---|---|---|---|
map | New array, same length | No | Transforming every element |
filter | New array, 0 to n elements | No | Keeping elements that pass a test |
find / findLast | One element or undefined | No | Looking up a single item |
some | Boolean | No | "Does at least one match?" |
every | Boolean | No | "Do all of them match?" |
reduce | Anything (number, object, ...) | No | Collapsing an array into one value |
forEach | undefined | No | Side effects only, no result needed |
sort / reverse | The same array | Yes | Reordering in place, deliberately |
toSorted / toReversed | New array | No | Reordering safely (ES2023) |
push / splice | Length / removed items | Yes | Building an array you own locally |
toSpliced / with | New array | No | Insert, delete, replace safely (ES2023) |
Common mistakes to avoid
Forgetting to return in map
An arrow function with braces needs an explicit return. Forget it and you get an array full of undefined:
// Bug: no return, result is [undefined, undefined, undefined]
const labels = cart.map((item) => {
`${item.name}: $${item.price}`;
});
// Fixed
const labelsFixed = cart.map((item) => `${item.name}: $${item.price}`);
If your mapped array is all undefined, this is the culprit ninety-nine times out of a hundred.
Using map for side effects
If you ignore the array map returns, you are using the wrong tool:
// Smell: map used as a loop
users.map((user) => sendEmail(user));
// Honest version
for (const user of users) {
sendEmail(user);
}
map is a promise to the reader that you are transforming data. Breaking that promise makes everyone reading the code look for a return value that does not matter.
Reduce abuse
Once reduce clicks, it is tempting to write everything with it. Resist. A reduce that filters and transforms at once is usually a filter plus a map in a trench coat, and much harder to read:
// Too clever
const names = users.reduce(
(acc, user) => (user.active ? [...acc, user.name] : acc),
[]
);
// Clear
const namesClear = users.filter((u) => u.active).map((u) => u.name);
The spread-into-accumulator version above also copies the array on every iteration. Save reduce for genuine aggregation.
Expecting index math to save you
Reaching for arr[arr.length - 1] still works, but arr.at(-1) says "last element" directly and handles negative indexes cleanly. Small thing, better sentence.
forEach vs for...of
Both run code for each element without producing a result, so which should you use?
forEach is fine for a quick side effect, but it has real limitations: you cannot break out of it, you cannot await properly inside it (the callback's promises are ignored), and return only skips one iteration.
for...of handles all of those naturally:
for (const item of cart) {
if (item.price > 1000) break; // early exit works
await saveItem(item); // sequential await works
}
A good default: methods (map, filter, reduce) when you want a result, for...of when you want actions, and forEach only for trivial one-liners where neither breaking nor awaiting will ever come up.
Frequently asked questions
Do array methods work on objects too?
Not directly, but Object.keys, Object.values, and Object.entries convert an object into an array you can then map or filter. Object.fromEntries converts back, so you can transform objects with an entries-map-fromEntries pipeline.
Are array methods slower than for loops?
Marginally, in most engines, because of the callback per element. For typical UI work (tens to thousands of items) the difference is noise. Write the readable version first and only switch to a loop if a profiler tells you that specific code is hot.
What is the difference between map and forEach?
map returns a new array built from your callback's return values. forEach returns undefined and exists purely for side effects. If you need the result, use map; if you do not, forEach or for...of.
Can I use async/await inside these methods?
Inside map, yes, with a pattern: an async callback returns promises, so you get an array of promises to pass to Promise.all. Inside filter, reduce, and forEach it gets awkward fast. For sequential async work over an array, for...of with await is the cleanest tool.
Where to go next
The six core methods plus the mutation rule will carry you through most JavaScript you write this year. The way to make them stick is the same as always: use them on real data until reaching for filter feels more natural than writing a loop.
If you want that practice with instant feedback, the JavaScript Fundamentals course has interactive lessons where you write and run this exact kind of code in the browser, from your first map to chained pipelines, with an AI tutor that hints instead of handing you answers.
Kodion Team
Kodion Editorial
Written by the team that builds Kodion's courses: working engineers who test every example in the interactive editor before it ships. How we create and review content
Continue Learning
π Related Articles
Python vs JavaScript: Which Should You Learn First in 2026?
An honest, hype-free comparison of Python and JavaScript for your first language: by goal, job market, difficulty, and what each is actually like to use.
TypeScript for Beginners: What It Is, Why It Won, and How to Learn It
TypeScript is now the default for serious JavaScript work. What it actually adds, when to learn it, and a gentle path from plain JavaScript to typed code.