A Practical Guide to JavaScript Array Methods

by Kuligaposten 2026-08-29

A tour of the most useful Array.prototype methods in JavaScript, grouped by what they do: searching, transforming, checking, ordering, and combining.

A Practical Guide to JavaScript Array Methods

Arrays are everywhere in JavaScript, and Array.prototype ships with dozens of methods for searching, transforming, and reshaping them. It's easy to only ever reach for map(), filter(), and a for loop and miss the rest. This post walks through the array methods you'll use most often, grouped by purpose, so you know which tool fits which job.

Mutating vs. Non-Mutating

Before diving in, one distinction matters more than any other: some array methods change the array in place, others return a new array (or value) and leave the original untouched.

const original = [3, 1, 2];

const sorted = [...original].sort(); // copy first, stays safe
console.log(original); // [3, 1, 2]

original.sort(); // mutates in place
console.log(original); // [1, 2, 3]

Knowing which category a method falls into avoids a whole class of bugs, especially when arrays are shared across functions or stored in state (e.g. in React).

Mutates the arrayReturns a new array / value
push, pop, shift, unshiftmap, filter, slice
splice, sort, reverseconcat, flat, flatMap
fill, copyWithinreduce, find, join

Adding and Removing Elements

const list = [1, 2, 3];

list.push(4); // [1, 2, 3, 4] - add to end
list.pop(); // [1, 2, 3]     - remove from end
list.unshift(0); // [0, 1, 2, 3] - add to start
list.shift(); // [1, 2, 3]     - remove from start

splice() is the general-purpose tool for inserting or removing elements anywhere in the array:

const months = ["Jan", "March", "April", "June"];

// splice(startIndex, deleteCount, ...itemsToInsert)
months.splice(1, 0, "Feb"); // insert without deleting
console.log(months); // ['Jan', 'Feb', 'March', 'April', 'June']

months.splice(4, 1, "May"); // replace one item
console.log(months); // ['Jan', 'Feb', 'March', 'April', 'May']

If you want the non-mutating equivalent, use slice() combined with spread:

const withMay = [...months.slice(0, 4), "May", ...months.slice(4)];

Searching and Checking

These methods answer questions about an array without transforming it.

const users = [
  { id: 1, name: "Ana", active: true },
  { id: 2, name: "Bo", active: false },
  { id: 3, name: "Cy", active: true },
];

users.find((u) => !u.active); // { id: 2, name: 'Bo', active: false }
users.findIndex((u) => !u.active); // 1
users.some((u) => !u.active); // true  - at least one match
users.every((u) => u.active); // false - not all match
users.includes(users[0]); // true  - reference equality
  • find() / findIndex() return the first matching element (or its index), or undefined / -1 if nothing matches.
  • some() short-circuits on the first true; every() short-circuits on the first false. Both are efficient for large arrays because they stop early.
  • includes() checks for a value using SameValueZero equality (like ===, but NaN equals NaN). For primitives it's a quick existence check; for objects it only matches by reference.

Since ES2023, findLast() and findLastIndex() search from the end:

users.findLast((u) => u.active); // { id: 3, name: 'Cy', active: true }

Transforming Arrays

map(), filter(), and reduce() are covered in depth in their own posts on this blog, but here's the shape of each for reference:

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

numbers.map((n) => n * 2); // [2, 4, 6, 8, 10]     - same length, transformed
numbers.filter((n) => n % 2 === 0); // [2, 4]       - subset that matches
numbers.reduce((acc, n) => acc + n, 0); // 15        - collapsed to one value

Two less common but very handy transformers:

// flat(depth) - flattens nested arrays, depth defaults to 1
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]
nested.flat(Infinity); // fully flatten, any depth

// flatMap(fn) - map() then flatten one level, more efficient than doing both
const sentences = ["hello world", "foo bar"];
sentences.flatMap((s) => s.split(" ")); // ['hello', 'world', 'foo', 'bar']

Ordering

const scores = [40, 100, 1, 5, 25];

scores.sort(); // [1, 100, 25, 40, 5] - sorts as strings by default!
scores.sort((a, b) => a - b); // [1, 5, 25, 40, 100]  - ascending
scores.sort((a, b) => b - a); // [100, 40, 25, 5, 1]  - descending

The default sort() converts elements to strings and compares them lexicographically, which is almost never what you want for numbers. Always pass a comparator for numeric or custom sorting. sort() mutates the array; if you need to preserve the original, copy it first with [...scores] or use toSorted() (ES2023), which returns a new sorted array:

const ascending = scores.toSorted((a, b) => a - b);

reverse() (mutating) and toReversed() (non-mutating, ES2023) work the same way for flipping order.

Combining and Iterating

const a = [1, 2];
const b = [3, 4];

a.concat(b); // [1, 2, 3, 4]      - non-mutating
[...a, ...b]; // [1, 2, 3, 4]      - same result, spread syntax

a.join("-"); // '1-2'              - array to string
"1,2,3".split(","); // ['1', '2', '3'] - string to array (not an array method, but the common pairing)

a.forEach((n) => console.log(n)); // runs a callback per element, returns undefined

forEach() looks similar to map(), but it never returns anything useful and can't be chained. Reach for it only when you want side effects (logging, pushing into an external variable) rather than a transformed array.

Choosing the Right Method

NeedMethod
Transform every elementmap()
Keep some elementsfilter()
Collapse to one valuereduce()
Find one elementfind() / findIndex()
Check existenceincludes(), some()
Check all matchevery()
Run side effectsforEach()
Flatten nested arraysflat() / flatMap()
Reordersort() / toSorted()
Insert/remove anywheresplice()

A Chained Example

Real code often chains several of these together:

const orders = [
  { id: 1, total: 42.5, paid: true },
  { id: 2, total: 15.0, paid: false },
  { id: 3, total: 99.99, paid: true },
  { id: 4, total: 5.25, paid: true },
];

const totalPaid = orders
  .filter((order) => order.paid)
  .map((order) => order.total)
  .reduce((sum, total) => sum + total, 0);

console.log(totalPaid.toFixed(2)); // '147.74'

Each step does one clear thing: filter() narrows the data, map() extracts what's needed, and reduce() collapses it to a final result. This pipeline style is usually more readable than an equivalent for loop, though for very large arrays or hot code paths a single loop can be faster since it avoids creating intermediate arrays.

Conclusion

Most everyday array work boils down to a small set of methods: map, filter, reduce, find, some/every, and sort. Knowing which ones mutate the array and which return a fresh copy will save you from a lot of subtle bugs, and knowing the less common ones — flat, flatMap, findLast, toSorted — means you won't reinvent them by hand. Pick the method that names your intent, and the code will read closer to the problem you're solving.

Back to Home