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 valuesnew Array(3): creates 3 empty slots (not [3]!), confusingArray.of(3): creates[3], not 3 empty slotsArray.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.
Press Run to execute the code and see output here.
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 elementarr[arr.length - 1]: last element (classic)arr.at(-1): last element (modern, ES2022)- Out-of-range index returns
undefined: no error lengthis writable truncating it removes elements
Indexing and length
at(-1) is cleaner than arr[arr.length - 1] for the last element.
Press Run to execute the code and see output here.
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 toundefinedbut 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.
Press Run to execute the code and see output here.
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 lengthpop(): removes from end, returns removed elementunshift(v): adds to start, returns new lengthshift(): 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).
Press Run to execute the code and see output here.
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 itemssplice: returns the removed elementsslice(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.
Press Run to execute the code and see output here.
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 equivalentindexOf(v): returns first index or-1lastIndexOf(v): returns last index or-1includes(v): returns boolean; correctly handlesNaN(indexOf cannot)
concat, indexOf, includes
includes() handles NaN correctly, indexOf cannot, making includes() the safer existence check.
Press Run to execute the code and see output here.
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 separatorreverse(): reverses in place, returns same arraysort(): default sorts as strings:[10, 9, 2].sort()→[10, 2, 9]sort((a, b) => a - b): numeric ascendingsort((a, b) => b - a): numeric descending
join, reverse, sort
Always pass (a, b) => a - b to sort numbers, the default treats everything as strings.
Press Run to execute the code and see output here.
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, returnsundefined; cannot breakmap(fn): returns a new array of transformed values; original unchangedfilter(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.
Press Run to execute the code and see output here.
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.
Press Run to execute the code and see output here.
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, orundefinedfindIndex(fn): returns index of first match, or-1some(fn):trueif at least one element passesevery(fn):trueonly if ALL elements pass
find, findIndex, some, every
some/every short-circuit, every() stops at the first false, some() at the first true.
Press Run to execute the code and see output here.
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 defaultflat(2): flattens two levels;flat(Infinity): all levelsflatMap(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.
Press Run to execute the code and see output here.
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-likeArray.of(...values): creates array from arguments, avoids constructor trapfill(value, start, end): fills a range with a value in placecopyWithin(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.
Press Run to execute the code and see output here.
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]]orArray.from({length:3}, ()=>[]) - Access:
matrix[0][1]: row 0, column 1 - Iterate: nested
forloops orflat()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.
Press Run to execute the code and see output here.
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.
Press Run to execute the code and see output here.
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 asa.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.
Press Run to execute the code and see output here.
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?