Overview

Modern JavaScript provides a rich set of array methods that replace manual loops. Learning them well makes code shorter, more declarative, and less error-prone. This tutorial covers the methods you will use every day, with examples and comparisons.

Why Use Array Methods

// Imperative loop
const doubled = [];
for (let i = 0; i < numbers.length; i++) {
  doubled.push(numbers[i] * 2);
}

// Declarative method
const doubled = numbers.map(n => n * 2);

The declarative version states what should happen, not how to iterate. It is shorter, avoids off-by-one errors, and composes cleanly with other methods.

map

Transforms each element and returns a new array of the same length.

const prices = [10, 20, 30];
const withTax = prices.map(p => p * 1.2);
// [12, 24, 36]

const users = [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }];
const names = users.map(u => u.name);
// ["Alice", "Bob"]

filter

Returns a new array containing only elements that pass a test.

const numbers = [1, 2, 3, 4, 5, 6];
const evens = numbers.filter(n => n % 2 === 0);
// [2, 4, 6]

const activeUsers = users.filter(u => u.active);

reduce

Combines all elements into a single value. The most flexible and often the most misunderstood method.

const numbers = [1, 2, 3, 4];

// Sum
const sum = numbers.reduce((acc, n) => acc + n, 0);
// 10

// Group by property
const orders = [
  { customer: "Alice", amount: 100 },
  { customer: "Bob", amount: 50 },
  { customer: "Alice", amount: 200 },
];

const totals = orders.reduce((acc, order) => {
  acc[order.customer] = (acc[order.customer] || 0) + order.amount;
  return acc;
}, {});
// { Alice: 300, Bob: 50 }

Always provide the initial value. Omitting it causes reduce to use the first element as the initial accumulator, which fails on empty arrays and can produce surprising types.

find and findIndex

const users = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" },
];

users.find(u => u.id === 2);        // { id: 2, name: "Bob" }
users.findIndex(u => u.id === 2);   // 1
users.find(u => u.id === 99);       // undefined

some and every

const numbers = [2, 4, 6, 8];

numbers.some(n => n > 7);   // true
numbers.some(n => n > 10);  // false
numbers.every(n => n % 2 === 0); // true
MethodReturnsShort-circuits
someBooleanYes, on first true
everyBooleanYes, on first false

flat and flatMap

const nested = [[1, 2], [3, 4], [5]];
nested.flat();       // [1, 2, 3, 4, 5]
nested.flat(2);      // flatten two levels

const sentences = ["hello world", "foo bar"];
sentences.flatMap(s => s.split(" "));
// ["hello", "world", "foo", "bar"]

sort

sort mutates the array and converts elements to strings by default. This causes the classic [10, 9, 80].sort() bug.

const nums = [10, 9, 80];
nums.sort();               // [10, 80, 9] — string comparison
nums.sort((a, b) => a - b); // [9, 10, 80] — numeric

// Non-mutating alternative
const sorted = [...nums].sort((a, b) => a - b);

Array Method Comparison

MethodReturnsMutates original
mapNew array, same lengthNo
filterNew array, shorter or equalNo
reduceAny valueNo
findElement or undefinedNo
some / everyBooleanNo
sortSame array, sortedYes
reverseSame array, reversedYes
spliceRemoved elementsYes
sliceNew array copyNo
push / popNew length / removed elementYes

Chaining Methods

const orders = [
  { customer: "Alice", amount: 100, status: "completed" },
  { customer: "Bob", amount: 50, status: "pending" },
  { customer: "Alice", amount: 200, status: "completed" },
];

const aliceTotal = orders
  .filter(o => o.customer === "Alice")
  .filter(o => o.status === "completed")
  .map(o => o.amount)
  .reduce((sum, amount) => sum + amount, 0);
// 300

Chaining is readable when each step is a clear transformation. If a chain grows beyond four or five methods, consider extracting helper functions or intermediate variables for clarity.

Performance Notes

  • Each chained method creates a new array. For very large datasets, combine passes or use a for loop.
  • find and some short-circuit; map and filter always visit every element.
  • Do not use map when you only need side effects; use forEach instead.
  • Do not use filter(...).length > 0 when some will do.