JavaScriptArray Methods

Array Methods

Arrays come with a generous set of built-in methods. The trick to using them well is to keep one distinction in mind: some methods mutate the array in place (and return something convenient like the new length), others return a new array and leave the original alone. Mix the two up and you get bugs that look like spooky action at a distance.

Adding and removing from the ends

These four are the bread-and-butter mutators. push and pop work at the end of the array (cheap); shift and unshift work at the start (and have to re-index every element, so are slower on large arrays).

JS
const stack = [1, 2, 3];

stack.push(4);          // returns 4 (new length).  stack: [1,2,3,4]
stack.push(5, 6);       // returns 6.               stack: [1,2,3,4,5,6]

const last = stack.pop();   // returns 6.           stack: [1,2,3,4,5]

const first = stack.shift();   // returns 1.        stack: [2,3,4,5]
stack.unshift(0, 1);            // returns 6.       stack: [0,1,2,3,4,5]
Use a real queue for high throughput
`shift` on a 100k-element array is much slower than `pop` because every remaining element shifts down one index. If you need a queue, model it with two stacks or use a linked-list-backed library.
splice — the swiss-army mutator

splice(start, deleteCount, ...items) is the most powerful (and most error-prone) mutator. It can insert, remove or replace anywhere in the array, and it returns the removed elements.

JS
const xs = ["a", "b", "c", "d", "e"];

// Remove 2 starting at index 1
const removed = xs.splice(1, 2);    // returns ["b", "c"]
// xs is now ["a", "d", "e"]

// Insert without removing (deleteCount = 0)
xs.splice(1, 0, "B", "C");          // xs: ["a", "B", "C", "d", "e"]

// Replace (remove 1, insert 1)
xs.splice(0, 1, "A");               // xs: ["A", "B", "C", "d", "e"]
splice mutates
If you reach for `splice` inside a React render or anywhere that expects pure data, you are likely creating bugs. Use `toSpliced` (a newer method) or build a new array with spread.
slice — the non-mutating cousin

slice(start, end) returns a new array containing a shallow copy of the original from start (inclusive) to end (exclusive). Negative indices count from the end. It does not change the source.

JS
const xs = [10, 20, 30, 40, 50];

xs.slice(1, 3);     // [20, 30]
xs.slice(2);        // [30, 40, 50]
xs.slice(-2);       // [40, 50]
xs.slice();         // a full shallow copy
xs;                 // unchanged: [10, 20, 30, 40, 50]
concat and join

concat glues arrays (or single values) into a new array. Today most code reaches for spread ([...a, ...b]) instead, but concat is still handy when one of the arguments might be a single value or an array.

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

a.concat(b);         // [1, 2, 3, 4]
a.concat(b, [5, 6]); // [1, 2, 3, 4, 5, 6]
a.concat(7, 8);      // [1, 2, 7, 8]  (single values allowed)

// Spread version (preferred in modern code)
[...a, ...b, 7];     // [1, 2, 3, 4, 7]

join(separator) turns an array into a string. The default separator is a comma; pass an explicit string to control the output.

JS
["one", "two", "three"].join();       // "one,two,three"
["one", "two", "three"].join(" / ");  // "one / two / three"
[1, [2, 3], 4].join("-");             // "1-2,3-4"  (nested arrays toString themselves)
reverse — mutating, with a non-mutating sibling

JS
const xs = [1, 2, 3];

xs.reverse();           // mutates: xs is now [3, 2, 1], also returned
const ys = [1, 2, 3];
const zs = ys.toReversed(); // returns a new array, leaves ys alone
ys;                          // [1, 2, 3]
zs;                          // [3, 2, 1]
flat and flatMap

flat(depth = 1) flattens nested arrays. flatMap(fn) is map followed by flat(1) — perfect when each element produces zero, one, or many results.

JS
[1, [2, 3], [4, [5, 6]]].flat();        // [1, 2, 3, 4, [5, 6]]
[1, [2, 3], [4, [5, 6]]].flat(2);       // [1, 2, 3, 4, 5, 6]
[1, [2, 3], [4, [5, 6]]].flat(Infinity); // fully flat

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

// Use flatMap to filter + map in one pass
const nums = [1, 2, 3, 4];
nums.flatMap(n => n % 2 ? [n * 10] : []); // [10, 30]
at — Python-style negative indexing

at(i) reads an element by index, but accepts negative indices that count from the end. It is the modern, readable replacement for arr[arr.length - 1].

JS
const xs = ["a", "b", "c", "d"];

xs.at(0);     // "a"
xs.at(-1);    // "d"   — last element
xs.at(-2);    // "c"
xs[-1];       // undefined — old indexing does not do negatives
Mutating vs non-mutating — a cheat sheet
  • Mutate in place: push, pop, shift, unshift, splice, sort, reverse, fill, copyWithin.

  • Return a new array: slice, concat, flat, flatMap, map, filter, toSorted, toReversed, toSpliced, with.

  • Mostly read-only: at, indexOf, lastIndexOf, includes, find, findIndex, some, every, join, forEach.

When in doubt, prefer non-mutating
Non-mutating methods compose naturally, work well with React state, and are easier to reason about. Reach for the mutating versions only when you have measured an actual performance problem.