JavaScript Array Methods
The iteration methods are the single most powerful thing about working with JavaScript arrays. Master map, filter, and reduce and most data transformations become one-liners. This chapter walks through every method you’ll actually use, when to pick each, and how to chain them safely.
Mental Model
Every iteration method takes a callback that runs once per element. The callback receives (value, index, wholeArray) — you usually only need value.
arr.METHOD((value, index, array) => { /* … */ });
Whether the method mutates the original or returns a new array is the single most important property:
| Returns new array | Returns one value | Mutates in place |
|---|---|---|
map, filter, slice, concat, flat, flatMap, toReversed, toSorted, toSpliced, with |
reduce, reduceRight, find, findIndex, findLast, findLastIndex, some, every, indexOf, includes, join |
push, pop, shift, unshift, splice, sort, reverse, fill, copyWithin |
forEach — Side Effects Only
Runs a function for every element. Returns undefined. Cannot be broken out of.
const names = ["Ada", "Lin", "Rae"];
names.forEach((name, i) => {
console.log(`${i}: ${name}`);
});
// Output:
// 0: Ada
// 1: Lin
// 2: Rae
If you need to stop early, use for…of with break or use some/every.
map — Transform Each Element
Returns a new array of the same length, where each element is the result of the callback.
[1, 2, 3].map(x => x * 2);
// Output: [2, 4, 6]
const users = [{ name: "Ada" }, { name: "Lin" }];
users.map(u => u.name);
// Output: ["Ada", "Lin"]
// Index is useful occasionally
["a", "b", "c"].map((v, i) => `${i}-${v}`);
// Output: ["0-a", "1-b", "2-c"]
[1, 2, 3, 4]
│ map(x => x * 2)
▼
[2, 4, 6, 8]
Tip: Use map when the shape stays the same but each value is transformed. If your callback returns undefined for some inputs, you probably wanted filter or flatMap instead.
filter — Keep Matching Elements
Returns a new array containing only the elements where the callback returns truthy.
[1, 2, 3, 4, 5].filter(x => x % 2 === 0);
// Output: [2, 4]
const users = [
{ name: "Ada", active: true },
{ name: "Lin", active: false },
{ name: "Rae", active: true },
];
users.filter(u => u.active);
// Output: [{ name: "Ada", active: true }, { name: "Rae", active: true }]
[1, 2, 3, 4, 5]
│ filter(x => x % 2 === 0)
▼
[2, 4]
reduce — Collapse to a Single Value
reduce((accumulator, current, index, array) => newAccumulator, initialValue)
// Sum
[1, 2, 3, 4].reduce((sum, x) => sum + x, 0);
// Output: 10
// Build an object
const items = ["apple", "banana", "apple", "cherry", "apple"];
items.reduce((counts, item) => {
counts[item] = (counts[item] ?? 0) + 1;
return counts;
}, {});
// Output: { apple: 3, banana: 1, cherry: 1 }
Walk-through of [1,2,3,4].reduce((s,x) => s+x, 0):
| Step | acc | current | returns |
|---|---|---|---|
| 1 | 0 | 1 | 1 |
| 2 | 1 | 2 | 3 |
| 3 | 3 | 3 | 6 |
| 4 | 6 | 4 | 10 |
Warning: Always provide an initialValue. Without it, the first element becomes the accumulator and the callback runs length - 1 times — usually not what you want, and it throws on an empty array.
reduceRight is the same but iterates from right to left.
find / findIndex
Return the first matching element (or its index), or undefined / -1 if none match. Stops as soon as one is found.
const users = [
{ id: 1, name: "Ada" },
{ id: 2, name: "Lin" },
{ id: 3, name: "Rae" },
];
users.find(u => u.id === 2); // { id: 2, name: "Lin" }
users.findIndex(u => u.id === 2); // 1
users.find(u => u.id === 99); // undefined
findLast / findLastIndex (ES2023) search from the end.
| Want | Use |
|---|---|
| Does a match exist? | some (returns boolean) |
| Get the matching element | find |
| Get its index | findIndex |
| Get all matches | filter |
some and every — Boolean Tests
const nums = [1, 2, 3, 4];
nums.some(x => x > 3); // true ← at least one
nums.some(x => x > 10); // false
nums.every(x => x > 0); // true ← all of them
nums.every(x => x > 2); // false
// Vacuous truth: every() of an empty array is TRUE
[].every(x => x > 0); // true
[].some(x => x > 0); // false
Both short-circuit (stop as soon as the answer is known).
sort and toSorted
sort mutates and returns the array. toSorted (ES2023) returns a new sorted array, leaving the original untouched.
const arr = [3, 1, 2];
arr.sort(); // mutates → [1, 2, 3]
arr.sort((a, b) => b - a); // → [3, 2, 1]
const sorted = arr.toSorted(); // new array, original unchanged
// Sorting objects
users.toSorted((a, b) => a.age - b.age);
users.toSorted((a, b) => a.name.localeCompare(b.name));
Comparator rules:
| Return | Effect |
|---|---|
< 0 |
a before b |
0 |
order preserved (stable since ES2019) |
> 0 |
a after b |
flat and flatMap
[1, [2, [3, [4]]]].flat(); // [1, 2, [3, [4]]]
[1, [2, [3, [4]]]].flat(Infinity); // [1, 2, 3, 4]
["one two", "three"].flatMap(s => s.split(" "));
// Output: ["one", "two", "three"]
// Drop some, keep some — return [] to drop, [x, y] to expand
[1, 2, 3, 4].flatMap(x => x % 2 === 0 ? [x * 10] : []);
// Output: [20, 40]
flatMap = map followed by flat(1) — one of the most underrated methods.
join and concat
["a", "b", "c"].join(); // "a,b,c" ← default separator
["a", "b", "c"].join(" - "); // "a - b - c"
["a", "b", "c"].join(""); // "abc"
[1, 2, 3].concat([4, 5]); // [1, 2, 3, 4, 5]
Prefer spread for concat: [...a, ...b].
Non-Mutating Companions (ES2023)
These were added so you can keep arrays immutable in functional code:
| Mutating | Non-mutating equivalent |
|---|---|
reverse() |
toReversed() |
sort() |
toSorted() |
splice() |
toSpliced() |
arr[i] = v |
arr.with(i, v) |
const arr = [1, 2, 3];
arr.with(1, 99); // [1, 99, 3] ← arr unchanged
arr.toSpliced(1, 1); // [1, 3]
arr.toReversed(); // [3, 2, 1]
Chaining
The real power. Each non-mutating method returns a new array, so you can pipe them:
const orders = [
{ id: 1, total: 50, status: "paid" },
{ id: 2, total: 80, status: "open" },
{ id: 3, total: 120, status: "paid" },
{ id: 4, total: 30, status: "paid" },
{ id: 5, total: 200, status: "open" },
];
const paidTotal = orders
.filter(o => o.status === "paid")
.map(o => o.total)
.reduce((sum, t) => sum + t, 0);
console.log(paidTotal); // 200
The pipeline reads top-to-bottom: keep paid → grab totals → sum.
Warning: Each chained method allocates a new array. For very large datasets, a single classic loop may be faster than three chained passes. Premature optimisation though — write the chain first, profile, then refactor only if needed.
A Realistic Example — Group, Count, Rank
const sales = [
{ rep: "Ada", region: "EU", amount: 200 },
{ rep: "Lin", region: "US", amount: 350 },
{ rep: "Ada", region: "US", amount: 150 },
{ rep: "Rae", region: "EU", amount: 500 },
{ rep: "Lin", region: "EU", amount: 100 },
{ rep: "Ada", region: "APAC", amount: 75 },
];
// Total per rep, sorted by amount desc
const ranking = Object.entries(
sales.reduce((tot, s) => {
tot[s.rep] = (tot[s.rep] ?? 0) + s.amount;
return tot;
}, {})
)
.map(([rep, total]) => ({ rep, total }))
.sort((a, b) => b.total - a.total);
console.log(ranking);
// [
// { rep: "Rae", total: 500 },
// { rep: "Lin", total: 450 },
// { rep: "Ada", total: 425 },
// ]
reduce to aggregate → Object.entries to convert object back to array → map to reshape → sort to rank.
Tip: ES2024 added Object.groupBy to handle the group-by step natively. The reduce pattern is still the most portable for now.
Pick-the-Right-Method Cheatsheet
| You want to… | Use |
|---|---|
| transform every element | map |
| keep some elements | filter |
| collapse to one value | reduce |
| run side effects, no result | forEach |
| find one element | find / findIndex |
| check if any/all match | some / every |
| flatten nested arrays | flat |
| map then flatten | flatMap |
| sort without mutating | toSorted |
| reverse without mutating | toReversed |
| make a string from an array | join |
Best Practices Checklist
- ✅ Prefer non-mutating methods (
map,filter,slice,toSorted) - ✅ Always pass
initialValuetoreduce - ✅ Pass a comparator to
sort— never trust the default for numbers - ✅ Use
findwhen you need one match;filterwhen you need many - ✅ Use
some/everyinstead offilter(...).length > 0 - ✅ Chain for readability — refactor for performance only if profiling shows it matters
- ❌ Don’t use
forEachwhenmaporfor…offits better - ❌ Don’t use
mappurely for side effects (useforEachorfor…of) - ❌ Don’t
awaitinsideforEach— it doesn’t wait. Usefor…oforPromise.all(map(...))
What’s Next?
Arrays handled. Now JavaScript’s other foundational structure — objects. Properties, methods, dot vs bracket access, shorthand syntax, and the first taste of this.