Overview

JavaScript arrays carry two families of methods: those that return a new array or value (functional, non-mutating) and those that modify the array in place (mutating). Mixing the two families without awareness causes bugs in React state and other frameworks where mutation bypasses change detection. This card groups methods by behavior. All examples are vanilla JavaScript; TypeScript types are inferred correctly for all of these.

Transform methods (return new array)

None of these modify the original array.

MethodSignatureReturnsNotes
maparr.map(fn)New array, same lengthTransform every element.
filterarr.filter(fn)New array, subsetKeep elements where fn returns truthy.
flatMaparr.flatMap(fn)New arraymap then one level of flat. Replaces arr.map(...).flat().
flatarr.flat(depth)New arrayFlatten nested arrays. Default depth is 1; Infinity fully flattens.
slicearr.slice(start, end)New arrayShallow copy of a range; negative indices count from end.
concatarr.concat(other)New arrayJoin two or more arrays; does not mutate either side.
toSortedarr.toSorted(fn)New arrayNon-mutating sort (ES2023).
toReversedarr.toReversed()New arrayNon-mutating reverse (ES2023).
toSplicedarr.toSpliced(start, del, ...items)New arrayNon-mutating splice (ES2023).
witharr.with(index, value)New arrayNon-mutating element replacement (ES2023).
const nums = [1, 2, 3, 4, 5]
 
nums.map((x) => x * 2) // [2, 4, 6, 8, 10]
nums.filter((x) => x % 2 === 0) // [2, 4]
nums.flatMap((x) => [x, -x]) // [1, -1, 2, -2, 3, -3, 4, -4, 5, -5]
;[
  [1, 2],
  [3, [4]],
].flat() // [1, 2, 3, [4]]
;[
  [1, 2],
  [3, [4]],
].flat(Infinity) // [1, 2, 3, 4]

Reduce and fold

Collapse an array to a single value.

MethodSignatureReturnsNotes
reducearr.reduce(fn, initial)Any valueLeft fold. Always pass an initial value; omitting it makes empty-array behavior undefined.
reduceRightarr.reduceRight(fn, initial)Any valueRight fold; rarely needed.
const sum = [1, 2, 3, 4].reduce((acc, x) => acc + x, 0) // 10
 
// Group by a key
const byStatus = items.reduce((acc, item) => {
  ;(acc[item.status] ??= []).push(item)
  return acc
}, {})

Search methods

Find elements or their positions.

MethodReturnsMutatesNotes
find(fn)First matching element or undefinedNoReturns the element itself.
findIndex(fn)Index or -1NoIndex of the first match.
findLast(fn)Last matching element or undefinedNoES2023; search from end.
findLastIndex(fn)Index or -1NoES2023.
indexOf(val)Index or -1NoStrict equality; no function; use for primitives.
includes(val)BooleanNoReturns true/false; handles NaN correctly (unlike indexOf).
some(fn)BooleanNoTrue if any element matches. Short-circuits.
every(fn)BooleanNoTrue if all elements match. Short-circuits.
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.some((u) => u.name === "Bob") // true
users.every((u) => u.id > 0) // true
;[NaN].includes(NaN) // true
;[NaN].indexOf(NaN) // -1 (indexOf does not handle NaN)

Iteration methods

Run a side effect per element.

MethodReturnsMutatesNotes
forEach(fn)undefinedNoFor side effects; cannot break early; cannot return a value.
entries()Iterator of [index, value]NoUse in for...of loops.
keys()Iterator of indicesNo
values()Iterator of valuesNo
arr.forEach((item, i) => console.log(i, item))
 
for (const [i, v] of arr.entries()) {
  console.log(i, v)
}

Prefer for...of over forEach when you need break, continue, or await inside the loop. forEach does not propagate promise rejections.

Mutating methods

These change the array in place. Avoid with React state; create a copy first.

MethodReturnsWhat it does
push(...items)New lengthAppend to end.
pop()Removed elementRemove from end.
unshift(...items)New lengthPrepend to start.
shift()Removed elementRemove from start.
splice(start, del, ...items)Removed elementsInsert/delete/replace at index.
sort(fn)Same array (mutated)In-place sort; use toSorted for non-mutating.
reverse()Same array (mutated)In-place reverse; use toReversed for non-mutating.
fill(val, start, end)Same arrayFill a range with a value.
copyWithin(target, start)Same arrayCopy a slice to another position within the array.
// Safe React state update: copy before mutating
setItems((prev) => [...prev, newItem]) // push equivalent
setItems((prev) => prev.filter((x) => x.id !== id)) // delete equivalent

Access and structural methods

MethodReturnsNotes
at(index)Element or undefinedSupports negative indices: arr.at(-1) is last element.
lengthNumberProperty, not a method; settable to truncate.
Array.from(iterable)New arrayConvert any iterable or array-like.
Array.isArray(val)BooleanReliable type check; typeof returns "object" for arrays.
Array.of(...items)New arrayAvoids Array(3) ambiguity (creates hole-y array vs 3-element array).
join(sep)StringConcatenate elements with separator.

Common gotchas

  • sort() converts elements to strings by default. [10, 2, 1].sort() returns [1, 10, 2]. Always pass a comparator: arr.sort((a, b) => a - b).
  • forEach returns undefined. Assigning const result = arr.forEach(...) is always wrong.
  • map on a sparse array preserves holes. Array(3).fill(0).map(...) works; Array(3).map(...) produces holes.
  • reduce without an initial value throws on an empty array. Pass 0, [], or {} as the initial value.
  • at(-1) is cleaner than arr[arr.length - 1]. Both return undefined for empty arrays.
  • splice both mutates the array and returns the removed elements. The side-effect-plus-return pattern surprises people who expect it to work like slice.
  • The toSorted, toReversed, toSpliced, and with methods require Node 20+ and are not available in older browsers without a polyfill.