Essential JavaScript Array Methods
AI-assistedDrafted with AI assistance and reviewed before publishing; examples are version-pinned. Verify against official docs when in doubt.
There are dozens of array methods, but a dozen cover daily work. Sorting them by whether they mutate the original array prevents most “why did my data change” bugs.
Mutating versus non-mutating
| Mutates the array | Returns a new result |
|---|---|
push pop shift unshift |
map filter slice concat |
splice sort reverse |
reduce find some every |
fill copyWithin |
flat flatMap toSorted toReversed |
If you did not explicitly ask for in-place modification, use the right-hand column.
map: transform each element
const nums = [1, 2, 3];
const doubled = nums.map((n) => n * 2);
console.log(doubled); // [2, 4, 6]
console.log(nums); // [1, 2, 3]
The callback’s return values form a new array of the same length. If you also need to drop elements, use filter first or flatMap — returning undefined from map and filtering afterwards is a common anti-pattern.
filter: keep what matches
const nums = [1, 2, 3, 4, 5];
const evens = nums.filter((n) => n % 2 === 0);
console.log(evens); // [2, 4]
The callback must return a boolean (or something truthy). filter never changes the length of the elements it keeps.
reduce: collapse to one value
const nums = [1, 2, 3, 4];
const total = nums.reduce((sum, n) => sum + n, 0);
console.log(total); // 10
Always pass the second argument. Without an initial value, reduce starts from the first element and throws TypeError on an empty array.
Grouping is the other classic use:
const words = ["apple", "ant", "banana", "berry"];
const groups = words.reduce((acc, word) => {
const key = word[0];
acc[key] ??= [];
acc[key].push(word);
return acc;
}, {});
console.log(groups);
// { a: ["apple", "ant"], b: ["banana", "berry"] }
reduce can express sums, counts, grouping, and flattening, but filter().map() is easier to read for filter-then-transform. In Node, Object.groupBy() (Node 21+) is even clearer for grouping.
find, findIndex, some, every
const users = [
{ id: 1, name: "ann", active: true },
{ id: 2, name: "bob", active: false },
];
users.find((u) => u.id === 2); // the object, or undefined
users.findIndex((u) => !u.active); // 1, or -1
users.some((u) => u.active); // true if at least one matches
users.every((u) => u.active); // false unless all match
some and every short-circuit once the answer is known, which makes them both faster and clearer than filter(...).length > 0.
includes and indexOf
["a", "b"].includes("b"); // true
["a", "b"].indexOf("b"); // 1, or -1
Use includes for existence and indexOf when the position matters. Both compare with ===, so objects compare by reference:
const a = { id: 1 };
[a].includes({ id: 1 }); // false, different object
[a].includes(a); // true
For objects, use some((x) => x.id === 1).
slice versus splice
const nums = [1, 2, 3, 4, 5];
nums.slice(1, 3); // [2, 3], original untouched
nums.splice(1, 2); // returns [2, 3], nums becomes [1, 4, 5]
slice(start, end) copies a range, end excluded. splice(start, deleteCount, ...items) edits in place and returns the removed elements.
The similar names cause real bugs; when reading code, check which one is used before assuming the array is intact.
sort and its string default
const nums = [10, 9, 1, 20];
nums.sort();
console.log(nums); // [1, 10, 20, 9]
sort() converts elements to strings and compares them lexicographically by default, so 9 lands after 20. Numbers require a comparator:
nums.sort((a, b) => a - b); // ascending: [1, 9, 10, 20]
nums.sort((a, b) => b - a); // descending
sort() mutates. To keep the original, use toSorted() (ES2023, Node 20+):
const sorted = nums.toSorted((a, b) => a - b); // nums unchanged
Also note that sort is stable in modern engines, so equal elements keep their relative order — useful when sorting by one field after another.
flat and flatMap
[[1, 2], [3, [4]]].flat(); // [1, 2, 3, [4]]
[[1, 2], [3, [4]]].flat(2); // [1, 2, 3, 4]
[[1, 2], [3, [4]]].flat(Infinity); // fully flattened
flatMap is map followed by one level of flattening, which fits “one item becomes many” or “one item becomes none”:
const lines = ["a,b", "c"];
lines.flatMap((line) => line.split(",")); // ["a", "b", "c"]
const nums = [1, 2, 3, 4];
nums.flatMap((n) => (n % 2 === 0 ? [n] : [])); // [2, 4]
Chaining
const orders = [
{ amount: 120, paid: true },
{ amount: 80, paid: false },
{ amount: 200, paid: true },
];
const total = orders
.filter((o) => o.paid)
.map((o) => o.amount)
.reduce((sum, n) => sum + n, 0);
console.log(total); // 320
Chains longer than three steps are easier to debug with intermediate const variables, and you get something to inspect in a debugger.
Common mistakes
1. sort() without a comparator
Numbers sort as strings. Always pass (a, b) => a - b for numbers.
2. reduce without an initial value
Throws on an empty array and surprises readers. Pass 0, {}, or [].
3. Confusing slice and splice
One copies, one mutates. The name difference is a single letter.
4. forEach plus push
That is a map. Using forEach to build an array hides the intent and invites bugs.
5. Splicing while iterating
Indices shift and elements are skipped. Use filter.
6. filter(...).length > 0 for existence
Use some(); it stops at the first match.
7. Chaining map and filter in the wrong order
Filter first to do less work, unless the transform produces the filter condition.
Summary
map/filter/reduce/find/slicereturn new values;splice/sort/reverse/pushmutate.- Numeric sorts need a comparator;
toSorted()keeps the original. - Always give
reducean initial value. some()for existence,find()for the element.
Next: objects, destructuring, and reference semantics.