JavaScript Array Methods

7 minute read

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 initialValue to reduce
  • ✅ Pass a comparator to sort — never trust the default for numbers
  • ✅ Use find when you need one match; filter when you need many
  • ✅ Use some/every instead of filter(...).length > 0
  • ✅ Chain for readability — refactor for performance only if profiling shows it matters
  • ❌ Don’t use forEach when map or for…of fits better
  • ❌ Don’t use map purely for side effects (use forEach or for…of)
  • ❌ Don’t await inside forEach — it doesn’t wait. Use for…of or Promise.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.

← Previous: JS Arrays Next: JS Objects →

Updated: