Arrays
map, filter, reduce, and the rest of the essential array API - plus the sort default that bites everyone once.
Arrays are ordered, zero-indexed lists. JavaScript's array methods let you transform, filter, and aggregate them without mutation - most of the time.
map transforms each element into a new value and returns a new array. filter keeps elements that pass a predicate. Neither mutates the original.
const prices = [10, 25, 8, 40, 15];
const doubled = prices.map((p) => p * 2);
console.log(doubled); // [20, 50, 16, 80, 30]
const affordable = prices.filter((p) => p < 20);
console.log(affordable); // [10, 8, 15]
// original is unchanged
console.log(prices); // [10, 25, 8, 40, 15]reduce accumulates elements into a single value - a sum, an object, another array. Always provide the initial accumulator to avoid surprising behavior on empty arrays.
const orders = [
{ product: "book", qty: 2, price: 12 },
{ product: "pen", qty: 5, price: 1.5 },
{ product: "desk", qty: 1, price: 200 },
];
const total = orders.reduce((sum, o) => sum + o.qty * o.price, 0);
console.log(total); // 231.5
// Group by first letter - reduce into an object
const byLetter = ["apple", "avocado", "banana", "blueberry"].reduce(
(acc, fruit) => {
const key = fruit[0];
acc[key] = acc[key] ?? [];
acc[key].push(fruit);
return acc;
},
{}
);
console.log(byLetter);
// { a: ['apple', 'avocado'], b: ['banana', 'blueberry'] }find returns the first matching element (or undefined). some and every return booleans. All three short-circuit on the first result.
const users = [
{ id: 1, name: "Ana", admin: true },
{ id: 2, name: "Bo", admin: false },
{ id: 3, name: "Cal", admin: true },
];
const ana = users.find((u) => u.id === 1);
console.log(ana?.name); // Ana
console.log(users.some((u) => u.admin)); // true
console.log(users.every((u) => u.admin)); // false.sort() mutates the array in place and defaults to lexicographic (string) comparison - even for numbers. Always pass a comparator for numeric sorts.
const nums = [10, 9, 2, 100, 21];
// WRONG - lexicographic sort
console.log([...nums].sort());
// [10, 100, 2, 21, 9]
// CORRECT - numeric comparator
console.log([...nums].sort((a, b) => a - b));
// [2, 9, 10, 21, 100]
// Sort mutates - spread first if you need the original
const sorted = [...nums].sort((a, b) => a - b);
console.log(nums); // [10, 9, 2, 100, 21] - untouched
console.log(sorted); // [2, 9, 10, 21, 100]flat and flatMap handle nested arrays. flat(depth) flattens to the given depth; flatMap is equivalent to map followed by flat(1) and is more efficient.
const matrix = [[1, 2], [3, 4], [5, 6]];
console.log(matrix.flat()); // [1, 2, 3, 4, 5, 6]
const sentences = ["hello world", "foo bar"];
const words = sentences.flatMap((s) => s.split(" "));
console.log(words); // ['hello', 'world', 'foo', 'bar']In production
The default .sort() comparator converts elements to strings before comparing, so [10, 9, 2, 100].sort() produces [10, 100, 2, 9] - a bug that staging almost never catches because test data is usually small and already sorted. Always supply (a, b) => a - b for numbers. And because .sort() mutates the original array, teams that want the unsorted original must spread first: [...arr].sort(...). Missing that spread is the second most common .sort bug, and it shows up in search and pagination code where the display order changes the underlying state.
Enjoyed this? Get more essays on software craft delivered to your inbox.
Subscribe free