Most work with data is the same few jobs: change every item, keep some items, find one item, or boil a list down to a single value. Arrays have a built-in method for each job. Using them makes your intent obvious in one word, where a for loop makes the reader work it out.
All examples in this lesson use this array:
const lessons = [
{ title: 'Variables', minutes: 12, level: 'Beginner', done: true },
{ title: 'Closures', minutes: 20, level: 'Intermediate', done: false },
{ title: 'Array methods', minutes: 25, level: 'Intermediate', done: false },
{ title: 'Functions', minutes: 20, level: 'Beginner', done: true },
];
How callbacks work here
Each method takes a callback and calls it once per item, passing three arguments: the item, its index and the whole array. You usually need only the first.
lessons.forEach((lesson, index) => {
console.log(`${index + 1}. ${lesson.title}`);
});
// → 1. Variables
// → 2. Closures
// → 3. Array methods
// → 4. Functions
forEach runs code for each item and returns undefined. Use it for side effects such as logging or updating the page. When you want a result, use one of the methods below.
map: transform every item
map returns a new array of the same length, built from whatever the callback returns:
const titles = lessons.map((lesson) => lesson.title);
console.log(titles);
// → ['Variables', 'Closures', 'Array methods', 'Functions']
const labels = lessons.map((lesson) => `${lesson.title} (${lesson.minutes} min)`);
console.log(labels[0]); // → Variables (12 min)
If you forget to return inside a callback with braces, every item becomes undefined:
const oops = lessons.map((lesson) => { lesson.title; });
console.log(oops); // → [undefined, undefined, undefined, undefined]
filter: keep some items
filter returns a new array containing only the items for which the callback returns a truthy value:
const todo = lessons.filter((lesson) => !lesson.done);
console.log(todo.length); // → 2
const beginner = lessons.filter((lesson) => lesson.level === 'Beginner');
console.log(beginner.map((lesson) => lesson.title));
// → ['Variables', 'Functions']
If nothing matches, you get an empty array, never undefined.
find and findIndex: get one item
find returns the first matching item, or undefined if none matches. It stops as soon as it finds one.
const closures = lessons.find((lesson) => lesson.title === 'Closures');
console.log(closures.minutes); // → 20
const missing = lessons.find((lesson) => lesson.title === 'Regex');
console.log(missing); // → undefined
findIndex returns the position instead, or -1. findLast and findLastIndex search from the end and are supported in all current browsers and Node.js 20.
some, every and includes: yes or no
console.log(lessons.some((lesson) => lesson.done)); // → true
console.log(lessons.every((lesson) => lesson.minutes < 30)); // → true
const levels = ['Beginner', 'Intermediate', 'Advanced'];
console.log(levels.includes('Advanced')); // → true
some answers "is at least one?", every answers "are all?", and includes checks for a value using strict equality (with the one exception that it does find NaN). Use includes for simple values and some when you need a condition.
reduce: boil down to one value
reduce walks the array carrying an accumulator. The callback receives the accumulator and the current item, and returns the new accumulator. The second argument to reduce is the starting value.
const totalMinutes = lessons.reduce((total, lesson) => total + lesson.minutes, 0);
console.log(totalMinutes); // → 77
Step by step: 0 + 12, then 12 + 20, then 32 + 25, then 57 + 20.
The accumulator can be any type. Here it is an object that counts lessons per level:
const perLevel = lessons.reduce((counts, lesson) => {
counts[lesson.level] = (counts[lesson.level] ?? 0) + 1;
return counts;
}, {});
console.log(perLevel); // → { Beginner: 2, Intermediate: 2 }
The ?? operator supplies 0 the first time a level appears; lesson 25 covers it.
Always pass a starting value. Without one, reduce uses the first item as the accumulator and throws a TypeError on an empty array.
reduce can do anything, which is exactly why it can be hard to read. If map, filter or a short for...of loop says the same thing, prefer those. For grouping, Object.groupBy(lessons, (lesson) => lesson.level) is a newer built-in for exactly this job. It is in all current browsers but not in Node.js 20, so check support on MDN before relying on it.
Chaining
Because map and filter return arrays, you can chain them into a pipeline that reads top to bottom:
const remaining = lessons
.filter((lesson) => !lesson.done)
.map((lesson) => lesson.title)
.join(', ');
console.log(remaining); // → Closures, Array methods
Each step creates a new array. For the lists you show on a page, that cost is negligible; readability matters more.
Sorting
sort takes a compare function that returns a negative number if a comes first, a positive number if b comes first, and 0 if they are equal:
const byLength = [...lessons].sort((a, b) => a.minutes - b.minutes);
console.log(byLength.map((lesson) => lesson.minutes)); // → [12, 20, 20, 25]
const byTitle = [...lessons].sort((a, b) => a.title.localeCompare(b.title));
console.log(byTitle[0].title); // → Array methods
Without a compare function, sort converts items to strings and compares them, so numbers sort wrongly:
console.log([10, 9, 1].sort()); // → [1, 10, 9]
Which methods mutate?
Some methods change the array they are called on. That matters when other code holds the same array (lesson 40 explains why).
| Changes the original | Returns a new array or value |
|---|---|
push, pop, shift, unshift | map, filter, slice, concat |
splice | toSpliced |
sort | toSorted |
reverse | toReversed |
fill, copyWithin | with(index, value) |
The copying versions toSorted, toReversed, toSpliced and with were added in ES2023 and are supported in all current browsers and Node.js 20. They replace the [...array].sort() pattern used above:
const sorted = lessons.toSorted((a, b) => a.minutes - b.minutes);
console.log(lessons[0].title); // → Variables (unchanged)
console.log(sorted[0].title); // → Variables (shortest first)
"New array" means a new outer array. The objects inside are the same objects. Changing sorted[0].done also changes lessons[0].done.
A few more you will use
flat()flattens nested arrays one level;flatMap()maps and flattens in one go.at(-1)reads the last item.Array.from(nodeList)turns array-like values, such as a NodeList, into a real array.join(', ')turns an array into a string.
Summary
maptransforms,filterkeeps,findreturns one item, andsome,everyandincludesanswer yes or no.reducefolds an array into one value; always give it a starting value.- Chain non-mutating methods into readable pipelines.
sortneeds a compare function for numbers, and it changes the original; prefertoSorted.- Know which methods mutate, and remember that copies of arrays still share their objects.