Skip to content
elephantoo

Array methods

Lesson 10 of 34 18 min read

map, filter, reduce, find, some/every, sort and the new non-mutating toSorted & friends.


Loops work, but modern JavaScript mostly processes arrays with array methods. Each method takes a callback describing what to do with each element, leading to short, readable, chainable code. We'll use this data throughout:

JavaScript
const products = [
  { id: 1, name: "Laptop", price: 55000, category: "tech", inStock: true },
  { id: 2, name: "Headphones", price: 2500, category: "tech", inStock: false },
  { id: 3, name: "Notebook", price: 120, category: "stationery", inStock: true },
  { id: 4, name: "Desk lamp", price: 1800, category: "home", inStock: true },
];

The callback for most methods receives (element, index, array). You usually only need the first one.

(Every example below assumes products is defined as above.)

map: transform every element#

map returns a new array of the same length, with each element replaced by your callback's return value:

JavaScript
const names = products.map((p) => p.name);
console.log(names); // [ 'Laptop', 'Headphones', 'Notebook', 'Desk lamp' ]

const withGst = products.map((p) => ({ ...p, price: Math.round(p.price * 1.18) }));
console.log(withGst[2]); // { id: 3, name: 'Notebook', price: 142, category: 'stationery', inStock: true }

console.log([1, 2, 3].map((n, i) => n * i)); // [ 0, 2, 6 ]

Notice that withGst creates new objects with spread instead of changing the originals.

filter: keep what passes a test#

filter returns a new array with only the elements where the callback returned something truthy:

JavaScript
const available = products.filter((p) => p.inStock);
console.log(available.length); // 3

const cheapTech = products.filter((p) => p.category === "tech" && p.price < 10000);
console.log(cheapTech.map((p) => p.name)); // [ 'Headphones' ]

const words = ["", "hello", null, "world", undefined];
console.log(words.filter(Boolean)); // [ 'hello', 'world' ] – drops falsy values

reduce: boil an array down to one value#

reduce(callback, initialValue) carries an accumulator through the array. Whatever the callback returns becomes the accumulator for the next element:

JavaScript
const total = products.reduce((sum, p) => sum + p.price, 0);
console.log(total); // 59420

// Step by step for [5, 10, 20] starting at 0:
// sum=0, n=5 → 5;  sum=5, n=10 → 15;  sum=15, n=20 → 35
console.log([5, 10, 20].reduce((sum, n) => sum + n, 0)); // 35

reduce can build any shape — here, counting products per category:

JavaScript
const perCategory = products.reduce((counts, p) => {
  counts[p.category] = (counts[p.category] ?? 0) + 1;
  return counts;
}, {});
console.log(perCategory); // { tech: 2, stationery: 1, home: 1 }

Always pass an initial value. Without it, reduce uses the first element — and throws a TypeError on an empty array. If a reduce becomes hard to read, a plain for...of loop is perfectly fine.

find, findIndex, findLast#

JavaScript
const lamp = products.find((p) => p.name === "Desk lamp");
console.log(lamp.price); // 1800

console.log(products.find((p) => p.price > 100000));  // undefined
console.log(products.findIndex((p) => p.id === 3));  // 2
console.log(products.findIndex((p) => p.id === 99)); // -1

const lastInStock = products.findLast((p) => p.inStock);
console.log(lastInStock.name); // Desk lamp

Use find when you want one item; filter always returns an array.

some and every#

JavaScript
console.log(products.some((p) => !p.inStock));      // true – at least one
console.log(products.every((p) => p.price > 100));  // true – all of them
console.log([].every((x) => x > 1000));             // true – vacuously true for empty arrays

Both stop as soon as they know the answer.

includes and indexOf#

For simple values, you don't need a callback:

JavaScript
const categories = products.map((p) => p.category);
console.log(categories.includes("home")); // true
console.log([NaN].includes(NaN));         // true (indexOf would return -1)

sort: ordering (carefully)#

sort has two traps: it mutates the array, and by default it compares elements as strings:

JavaScript
console.log([10, 9, 1, 100].sort()); // [ 1, 10, 100, 9 ] 😱

const nums = [10, 9, 1, 100];
nums.sort((a, b) => a - b);  // ascending
console.log(nums);           // [ 1, 9, 10, 100 ]
nums.sort((a, b) => b - a);  // descending
console.log(nums);           // [ 100, 10, 9, 1 ]

The compare function returns a negative number if a should come first, positive if b should, and 0 to keep their order. For strings, use localeCompare:

JavaScript
const byName = products.toSorted((a, b) => a.name.localeCompare(b.name));
console.log(byName.map((p) => p.name)); // [ 'Desk lamp', 'Headphones', 'Laptop', 'Notebook' ]

const byPriceDesc = products.toSorted((a, b) => b.price - a.price);
console.log(byPriceDesc[0].name); // Laptop

Non-mutating versions (ES2023)

MutatesReturns a new array
sort()toSorted()
reverse()toReversed()
splice()toSpliced()
arr[i] = xwith(i, x)
JavaScript
const original = [3, 1, 2];
const sorted = original.toSorted();
const replaced = original.with(0, 99);
console.log(original, sorted, replaced); // [ 3, 1, 2 ] [ 1, 2, 3 ] [ 99, 1, 2 ]

Prefer these in modern code — especially in React, where mutating state causes bugs.

forEach: just do something#

forEach runs a callback for each element and returns undefined. Use it for side effects like logging; use map when you want a new array:

JavaScript
products.forEach((p, i) => console.log(`${i + 1}. ${p.name}`));

You can't break out of forEach — use for...of, some or find if you need to stop early.

flat and flatMap#

JavaScript
console.log([[1, 2], [3], [4, [5]]].flat());         // [ 1, 2, 3, 4, [ 5 ] ]
console.log([[1, 2], [3, [4, [5]]]].flat(Infinity)); // [ 1, 2, 3, 4, 5 ]

const sentences = ["hello world", "array methods"];
console.log(sentences.flatMap((s) => s.split(" "))); // [ 'hello', 'world', 'array', 'methods' ]

Creating arrays#

JavaScript
console.log(Array.from({ length: 5 }, (_, i) => i * i)); // [ 0, 1, 4, 9, 16 ]
console.log(Array.from("abc"));   // [ 'a', 'b', 'c' ]
console.log(new Array(3).fill(0)); // [ 0, 0, 0 ]
console.log(Array.of(7));          // [ 7 ]

Chaining: putting it together#

JavaScript
const report = products
  .filter((p) => p.inStock)
  .toSorted((a, b) => a.price - b.price)
  .map((p) => `${p.name}: ₹${p.price.toLocaleString("en-IN")}`)
  .join("\n");

console.log(report);
Output
Notebook: ₹120
Desk lamp: ₹1,800
Laptop: ₹55,000

Read it like a sentence: keep in-stock products, sort by price, format each one, join into lines.

Grouping (ES2024)#

Object.groupBy (Node 21+, all current browsers) groups elements by a key:

JavaScript
const grouped = Object.groupBy(products, (p) => p.category);
console.log(Object.keys(grouped));           // [ 'tech', 'stationery', 'home' ]
console.log(grouped.tech.map((p) => p.name)); // [ 'Laptop', 'Headphones' ]

Cheat sheet#

I want to…MethodReturns
Transform each itemmapnew array (same length)
Keep some itemsfilternew array
Combine into one valuereduceanything
Get the first matchfind / findIndexitem / index
Ask "any?" / "all?"some / everyboolean
Order itemstoSorted / sortnew / same array
Do something for eachforEachundefined

Common mistakes#

  • Forgetting to return inside a braces callback: arr.map((x) => { x * 2 }) gives [undefined, …].
  • Sorting numbers without a compare function.
  • Using map just to loop (and ignoring its result) — use forEach or for...of.
  • Mutating objects inside map instead of returning new ones.

What's next#

You've mastered arrays. Next, go deeper into objects: shorthand syntax, computed keys, copying, freezing and more.

Check your understanding

Quick quiz

0/3 answered
  1. 1.What does [10, 9, 1].sort() return?

  2. 2.What is the result of [1, 2, 3, 4].reduce((sum, n) => sum + n, 0)?

  3. 3.What does find return when no element matches?

Finished reading?

Mark this lesson complete to track your progress.