Arrays, Iteration and Transformation
sort, sparse arrays, spread on big data).
Arrays are ordered lists
Literals, indexes and length
An array is an ordered collection — the workhorse data structure of JavaScript.
Positions start at 0; length is always one more than the last index:
const colors = ["red", "green", "blue"];
console.log(colors[0], colors.length); // red 3
colors.push("yellow"); // add at the end -> 4 items
colors.pop(); // remove from the end
colors.unshift("black"); // add at the START (slow on big arrays)
console.log(colors); // [ 'black', 'red', 'green', 'blue' ]
Arrays are objects with number keys — typeof colors is
"object"; test for a real array with Array.isArray(colors).
Mutating versus non-mutating — know which family you call
const scores = [3, 1, 2];
scores.push(4); // MUTATES: scores is now [3, 1, 2, 4]
console.log(scores.toSorted()); // [1, 2, 3, 4] — a COPY; original untouched
console.log(scores); // [3, 1, 2, 4] — original order survives
The mutating classics: push/pop/shift/unshift, splice,
sort, reverse. Their modern copy-first twins:
toSorted, toReversed, toSpliced, with.
In state-driven code (lesson 24) prefer the copies — shared arrays bite exactly like the
shared objects in lesson 04's value-versus-reference diagram.
The sort trap — a runnable contraexample
const numbers = [10, 9, 100, 1];
console.log(numbers.sort()); // [ 1, 10, 100, 9 ] — sorted as TEXT!
console.log([...numbers].sort((a, b) => a - b)); // [ 1, 9, 10, 100 ] — correct
The default sort() converts items to strings and compares characters:
"1" < "10" < "100" < "9". Always pass a compare function for numbers —
and remember sort() mutates, hence the spread copy.
The transformation pipeline
filter → map → reduce: the workhorse chain
const orders = [
{ id: 1, total: 25, paid: true },
{ id: 2, total: 120, paid: false },
{ id: 3, total: 80, paid: true },
];
const revenue = orders
.filter(order => order.paid) // keep paid orders
.map(order => order.total) // project to amounts
.reduce((sum, amount) => sum + amount, 0); // fold into ONE number
console.log(revenue); // 105
Each link returns a new array (or value) — no mutation, easy to test, reads like the sentence
"of the paid orders, take the totals and add them". The 0 is
reduce's starting accumulator. Single-question helpers:
find (first match), some (any true?), every (all true?).
When a plain loop wins
Chains run to the end; a for loop with break exits early, and complex
multi-step reductions often read better as a loop. Reach for the chain first, drop to a loop
when it gets in the way.
Sparse arrays — the hole trap
delete leaves a hole, not an undefined slot
const sparse = [1, 2, 3];
delete sparse[1];
console.log(1 in sparse); // false — a HOLE, not a value
console.log(sparse.length); // 3 — the hole still counts
console.log(sparse.map(x => "x")); // [ 'x', <1 empty item>, 'x' ] — skipped!
Holes make methods behave inconsistently (iteration skips them, length counts
them). Remove items with splice/toSpliced, or write
undefined explicitly instead of delete.
TypedArrays in one paragraph
Exact memory layout, fixed length
When you need raw binary — image bytes, audio, shared memory (lesson 22) — you leave normal
arrays for TypedArrays: new Float32Array(8) is a fixed-length
array of 8 real 32-bit numbers, backed by a contiguous buffer
(ArrayBuffer). They exist so memory layout is exact; the transformation pipelines
above do not apply to them directly.
Practice: the array lab
The task
Run foundations/16_arrays_lab.js with node, predicting every line —
especially the sort trap. Then take the orders array and produce, in one chain,
the largest paid total.
The checklist
- You can say for any method: does it mutate or copy?
- You can explain why
[10, 9, 100, 1].sort()puts 9 last. - You can build a filter/map/reduce chain for a small dataset.
- You know what
deletedoes to an array — and what to use instead.