JavaScript array methods are one of the best investments in your day-to-day productivity. Once you internalize map, filter, reduce, and their siblings, most data transformations become a few readable lines instead of nested for loops.
The Core Three
map — Transform Each Element
map returns a new array with each element transformed:
const prices = [10, 25, 8, 42];
const withTax = prices.map(price => price * 1.1);
// [11, 27.5, 8.8, 46.2]
const users = [{ name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }];
const names = users.map(user => user.name);
// ['Alice', 'Bob']
// Extract and rename
const simplified = users.map(({ name, age }) => ({ label: name, value: age }));
filter — Keep Elements Matching a Condition
filter returns a new array with only the elements where the callback returns true:
const numbers = [1, 2, 3, 4, 5, 6];
const evens = numbers.filter(n => n % 2 === 0);
// [2, 4, 6]
const users = [
{ name: 'Alice', active: true },
{ name: 'Bob', active: false },
{ name: 'Carol', active: true }
];
const active = users.filter(u => u.active);
// [{ name: 'Alice' ... }, { name: 'Carol' ... }]
reduce — Accumulate to a Single Value
reduce is the most versatile — it can implement any transformation, but it's most useful when you need to collapse an array into a single value:
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((acc, n) => acc + n, 0);
// 15
// Count occurrences
const fruits = ['apple', 'banana', 'apple', 'cherry', 'banana', 'apple'];
const counts = fruits.reduce((acc, fruit) => {
acc[fruit] = (acc[fruit] || 0) + 1;
return acc;
}, {});
// { apple: 3, banana: 2, cherry: 1 }
// Group by property
const events = [
{ type: 'click', x: 10 },
{ type: 'scroll', y: 50 },
{ type: 'click', x: 30 }
];
const grouped = events.reduce((acc, event) => {
(acc[event.type] = acc[event.type] || []).push(event);
return acc;
}, {});
// { click: [...], scroll: [...] }
Finding Elements
find and findIndex
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Carol' }
];
const bob = users.find(u => u.id === 2);
// { id: 2, name: 'Bob' }
const bobIndex = users.findIndex(u => u.id === 2);
// 1
// Returns undefined (not -1) if not found
const nobody = users.find(u => u.id === 99);
// undefined
some and every
const scores = [85, 92, 78, 95, 88];
const allPassing = scores.every(s => s >= 70); // true
const anyPerfect = scores.some(s => s === 100); // false
const anyFailing = scores.some(s => s < 70); // false
Flattening and Combining
flat and flatMap
const nested = [1, [2, 3], [4, [5, 6]]];
nested.flat(); // [1, 2, 3, 4, [5, 6]]
nested.flat(2); // [1, 2, 3, 4, 5, 6] — depth 2
nested.flat(Infinity); // flatten any depth
// flatMap = map + flat(1)
const sentences = ['hello world', 'foo bar'];
const words = sentences.flatMap(s => s.split(' '));
// ['hello', 'world', 'foo', 'bar']
// Useful for expanding items conditionally
const items = [{ type: 'product', qty: 2 }, { type: 'gift' }];
const lines = items.flatMap(item =>
item.qty > 1
? Array.from({ length: item.qty }, () => item)
: [item]
);
Sorting
sort mutates the array. Use a copy for immutability:
const numbers = [3, 1, 4, 1, 5, 9, 2, 6];
// Wrong: sort() without comparator sorts lexicographically
numbers.sort(); // [1, 1, 2, 3, 4, 5, 6, 9] — works here by accident
// Right: always use a comparator for numbers
numbers.sort((a, b) => a - b); // ascending
numbers.sort((a, b) => b - a); // descending
// Strings
const words = ['banana', 'apple', 'cherry'];
words.sort(); // alphabetical: ['apple', 'banana', 'cherry']
words.sort((a, b) => a.localeCompare(b)); // locale-aware
// Objects
const users = [{ name: 'Charlie' }, { name: 'Alice' }, { name: 'Bob' }];
users.sort((a, b) => a.name.localeCompare(b.name));
// Immutable sort (ES2023)
const sorted = numbers.toSorted((a, b) => a - b); // returns new array
Checking and Searching
const arr = [1, 2, 3, NaN];
arr.includes(2); // true
arr.includes(5); // false
arr.includes(NaN); // true ← unlike indexOf
arr.indexOf(2); // 1
arr.indexOf(5); // -1
arr.indexOf(NaN); // -1 ← NaN !== NaN
arr.lastIndexOf(2); // 1 (searches from end)
Array Construction
// Create array of length n
Array.from({ length: 5 }, (_, i) => i); // [0, 1, 2, 3, 4]
Array.from({ length: 5 }, () => 0); // [0, 0, 0, 0, 0]
// From iterable
Array.from('hello'); // ['h', 'e', 'l', 'l', 'o']
Array.from(new Set([1, 2, 2])); // [1, 2] — deduplicate
Array.from(nodeList); // NodeList to array
// Array.of
Array.of(1, 2, 3); // [1, 2, 3]
Mutation Methods (use carefully)
const arr = [1, 2, 3, 4, 5];
arr.push(6); // add to end, returns new length
arr.pop(); // remove from end, returns removed element
arr.unshift(0); // add to start, returns new length
arr.shift(); // remove from start, returns removed element
arr.splice(1, 2); // remove 2 elements at index 1, returns removed
arr.reverse(); // reverses in place
// Immutable alternatives (ES2023)
arr.toReversed(); // reversed copy
arr.toSpliced(1, 2); // spliced copy
arr.with(2, 99); // copy with arr[2] = 99
Chaining
Methods chain naturally since most return arrays:
const data = [
{ name: 'Alice', score: 92, active: true },
{ name: 'Bob', score: 65, active: false },
{ name: 'Carol', score: 88, active: true },
{ name: 'Dave', score: 45, active: true }
];
const result = data
.filter(u => u.active) // only active users
.filter(u => u.score >= 70) // only passing scores
.sort((a, b) => b.score - a.score) // sort by score descending
.map(u => `${u.name}: ${u.score}`) // format
.join(', ');
// "Alice: 92, Carol: 88"
Performance Note
For very large arrays (millions of items), for...of loops are faster than chained array methods because they avoid creating intermediate arrays. For typical application data (hundreds to thousands of items), the readability benefit of array methods outweighs any performance difference.
The immutable methods (map, filter, reduce) are also better for React state management — they return new references, which is what React's reconciler checks to detect changes.
Top comments (0)