JavaScript: Arrays

Master array creation, indexing, every built-in method, iteration patterns, destructuring, and the spread operator.

Array Creation

Arrays are most commonly created with the literal syntax []. The Array constructor exists but has confusing edge cases, prefer the literal or Array.from() / Array.of().

Array Creation

Always use [] for literals, the Array constructor has a one-argument trap.

  • []: empty array literal
  • [1, 2, 3]: array with initial values
  • new Array(3): creates 3 empty slots (not [3]!), confusing
  • Array.of(3): creates [3], not 3 empty slots
  • Array.from({length: 3}, (_, i) => i): creates [0, 1, 2]

Array Creation

new Array(3) creates 3 empty slots, Array.of(3) creates [3]. Use Array.from for generated arrays.

Accessing Elements and Length

Arrays are zero-indexed, the first element is at index 0. The length property reflects the count of elements. The new at() method supports negative indices cleanly.

Indexing and length

Zero-indexed, first is [0], last is [length - 1] or at(-1).

  • arr[0]: first element
  • arr[arr.length - 1]: last element (classic)
  • arr.at(-1): last element (modern, ES2022)
  • Out-of-range index returns undefined: no error
  • length is writable truncating it removes elements

Indexing and length

at(-1) is cleaner than arr[arr.length - 1] for the last element.

Modifying Arrays

Arrays are mutable, you can change elements by index, add new ones beyond the current length, or delete elements (which leaves an empty slot). Direct index assignment is the simplest form of modification.

Direct Modification

Assign by index to update or extend, delete leaves a sparse hole, not a shrunk array.

  • arr[1] = "new", replaces element at index 1
  • Assigning beyond length extends the array with empty slots
  • delete arr[i], sets the slot to undefined but does NOT reduce length
  • Use splice() to properly remove elements

Direct Modification

delete leaves a hole, use splice() to remove an element and keep length accurate.

push(), pop(), shift(), unshift()

These four methods add or remove elements from either end of an array. They all mutate the original array.

End and Start Mutation

push/pop work the end; shift/unshift work the start, all mutate in place.

  • push(v): adds to end, returns new length
  • pop(): removes from end, returns removed element
  • unshift(v): adds to start, returns new length
  • shift(): removes from start, returns removed element
  • shift/unshift are slower on large arrays, they re-index every element

push, pop, shift, unshift

push + pop = stack (LIFO). shift + push = queue (FIFO).

splice() and slice()

splice() mutates the array, it removes, replaces, or inserts elements in place. slice() is non-destructive, it returns a new sub-array without touching the original.

splice vs slice

splice mutates, slice copies. The one-letter difference matters enormously.

  • splice(start, deleteCount, ...items): removes deleteCount elements at start, optionally inserts items
  • splice: returns the removed elements
  • slice(start, end): returns a new array from start to end (exclusive)
  • slice(): with no args is a shallow clone of the whole array

splice vs slice

slice(-2) returns the last two elements, negative indices count from the end.

concat(), indexOf(), lastIndexOf(), includes()

concat() merges arrays into a new one. indexOf and lastIndexOf find a value's position. includes() returns a boolean. All are non-mutating.

Search and Merge

includes() for existence, indexOf() for position, concat() for merging, none mutate.

  • concat(arr2): merges, returns new array; spread [...a, ...b] is equivalent
  • indexOf(v): returns first index or -1
  • lastIndexOf(v): returns last index or -1
  • includes(v): returns boolean; correctly handles NaN (indexOf cannot)

concat, indexOf, includes

includes() handles NaN correctly, indexOf cannot, making includes() the safer existence check.

join(), reverse(), sort()

join() converts an array to a string. reverse() flips the array in place. sort() sorts in place, but its default comparison is lexicographic, which surprises developers sorting numbers.

join / reverse / sort

sort() and reverse() mutate in place, always pass a comparator to sort() for numbers.

  • join(sep): joins elements into a string with separator
  • reverse(): reverses in place, returns same array
  • sort(): default sorts as strings: [10, 9, 2].sort()[10, 2, 9]
  • sort((a, b) => a - b): numeric ascending
  • sort((a, b) => b - a): numeric descending

join, reverse, sort

Always pass (a, b) => a - b to sort numbers, the default treats everything as strings.

forEach(), map(), filter()

These three iteration methods are the workhorses of array processing. forEach runs a side effect per element, map transforms each element into a new array, and filter keeps only elements that pass a test.

forEach / map / filter

Three distinct purposes side effect, transform, and subset.

  • forEach(fn): runs fn for each element, returns undefined; cannot break
  • map(fn): returns a new array of transformed values; original unchanged
  • filter(fn): returns a new array of elements where fn returns truthy
  • Chain them: arr.filter(...).map(...)

forEach, map, filter

Chaining filter().map() is clean and readable, filter first, then transform.

reduce() and reduceRight()

reduce() accumulates all elements into a single value by passing each one into a callback along with a running accumulator. reduceRight() does the same but iterates from right to left.

reduce()

The most powerful iterator can implement map, filter, and any aggregation.

  • Signature: reduce((accumulator, current) => newAcc, initialValue)
  • Always provide an initial value omitting it uses the first element (fragile on empty arrays)
  • Accumulator starts as the initial value; each call returns the next accumulator
  • Can produce any type: number, string, object, array

reduce()

reduce() can produce any result type, here it builds a frequency map from an array of strings.

find(), findIndex(), some(), every()

These methods test elements against a predicate. find/findIndex return a specific element or its index. some/every return booleans and short-circuit once the result is determined.

find / findIndex / some / every

All short-circuit, they stop iterating as soon as the answer is known.

  • find(fn): returns the first matching element, or undefined
  • findIndex(fn): returns index of first match, or -1
  • some(fn): true if at least one element passes
  • every(fn): true only if ALL elements pass

find, findIndex, some, every

some/every short-circuit, every() stops at the first false, some() at the first true.

flat() and flatMap()

flat(depth) flattens nested arrays by the specified depth. flatMap(fn) maps each element then flattens one level equivalent to .map().flat(1) but more efficient.

flat() / flatMap()

flat collapses nesting; flatMap maps then flattens in one pass.

  • flat(): flattens one level by default
  • flat(2): flattens two levels; flat(Infinity): all levels
  • flatMap(fn): map + flat(1) in one efficient call
  • Useful when a map callback returns an array and you want a flat result

flat and flatMap

flatMap splits then flattens in one pass, map().flat() would produce the same result in two.

Array.from(), Array.of(), fill(), copyWithin()

These utility methods handle array creation from non-array sources and in-place filling. They cover use cases that the literal syntax cannot express concisely.

Modern Utility Methods

Construction and in-place filling, less common but useful when you need them.

  • Array.from(iterable, mapFn): array from any iterable or array-like
  • Array.of(...values): creates array from arguments, avoids constructor trap
  • fill(value, start, end): fills a range with a value in place
  • copyWithin(target, start, end): copies a slice to another position in place

from, of, fill, copyWithin

new Array(5).fill(0) is the cleanest way to initialise a fixed-length array of zeros.

Multi-dimensional Arrays

JavaScript has no native matrix type, multi-dimensional arrays are simply arrays of arrays. Access elements with chained bracket notation and iterate with nested loops or flat().

Multi-dimensional Arrays

Arrays of arrays, access with arr[row][col], iterate with nested loops.

  • Create: [[1,2],[3,4],[5,6]] or Array.from({length:3}, ()=>[])
  • Access: matrix[0][1]: row 0, column 1
  • Iterate: nested for loops or flat() then a single loop
  • Cloning: each row must be spread independently [...matrix] is a shallow copy

Multi-dimensional Arrays

matrix.flat() collapses all rows into one array, then reduce can sum them in one pass.

Array Destructuring

Destructuring extracts values from an array into named variables in one concise statement. It is especially useful with function return values and swapping variables.

Array Destructuring

Unpack array values into variables by position, concise and readable.

  • Syntax: const [a, b, c] = arr;
  • Skip elements with commas: const [, second,, fourth] = arr;
  • Default values: const [a = 0, b = 0] = arr;
  • Rest in destructuring: const [first, ...rest] = arr;
  • Swap without temp variable: [a, b] = [b, a];

Array Destructuring

[p, q] = [q, p] swaps two variables with no temp variable, a classic destructuring trick.

Spread Operator with Arrays

The spread operator ... expands an array into individual values. It enables cloning, merging, and passing arrays as function arguments without mutation.

Spread with Arrays

Expands array elements in place, the immutable alternative to push/concat/slice.

  • Clone: [...arr]: shallow copy, mutations don't affect original
  • Merge: [...a, ...b]: same as a.concat(b) but more readable
  • Insert: [...a, x, ...b]: merge with a value in the middle
  • Pass as args: Math.max(...nums)

Spread with Arrays

[...a] is a shallow clone, push on clone does not affect the original.

Knowledge Check

1. What does push() return?

2. What is the difference between slice() and splice()?

3. What does map() return?

4. What does reduce() do?

5. What does find() return when no element matches?

6. What is the result of [1, [2, [3]]].flat(Infinity)?

7. What does every() return?

8. Which method adds elements to the beginning of an array?

9. What does Array.from("hello") produce?

10. What does the spread operator do when used with an array?