Skip to main content

Menu

System onlineAI Tutorials / 2026

AI Tutorials
Knowledge search

Recent · Popular · Tutorials

  • dart3 tutorials
  • java1 tutorial
  • Javascript3 tutorials
  • Node.js Introduction2 tutorials
  • dartdart · General · In depth
  • JavascriptJavascript · Advanced · In depth
  • JavascriptJavascript · Advanced · Mid
  • JavascriptJavascript · Advanced · Short

Find a tutorial

Tell the assistant what you want to learn. It suggests published chapters.

For example: “I want to learn Rust ownership from the start.” The conversation is kept only in this tab.

0 / 2,000 characters

Enter to send, Shift+Enter for a new line. Do not include personal information.

Chapter 9

Arrays and Iteration Methods

Prerequisites: this chapter assumes you are comfortable with the previous material on values and types, functions, closures, and `this`. You should know that arrays are objects with numeric keys, that methods live on `Array.prototype`, and that arrow functions and ordinary functions differ in `this`
Level: AdvancedLength: In depth4 min read

Read at another depth

Arrays and Iteration Methods

Prerequisites: this chapter assumes you are comfortable with the previous material on values and types, functions, closures, and this. You should know that arrays are objects with numeric keys, that methods live on Array.prototype, and that arrow functions and ordinary functions differ in this binding. Here we go deep on how array methods actually work, where they differ from each other, and where they misbehave.

The working model

An array in JavaScript is not a distinct data structure at the engine level — it is an object whose keys are strings that look like integers, plus a length property that the engine keeps in sync. Most of the iteration methods share one contract, defined by the spec in terms of an internal "iterator protocol" over indexed elements:

  1. Read length.
  2. Visit indices 0 through length - 1.
  3. Skip holes (empty slots) for most methods.
  4. Call a callback with (element, index, array).

Two consequences follow immediately:

  • Callbacks get three arguments. parseInt takes a second radix argument, so ['1','7','11'].map(parseInt) produces [1, NaN, 3]. This is the classic trap: you passed a function whose signature does not match the callback contract.
  • The array is read at call time, not snapshot. Mutating it during iteration changes what later callbacks see.
Code example in javascript
const nums = [1, 2, 3];
nums.map((n) => n * 2);        // [2, 4, 6]
nums.forEach((n, i, arr) => console.log(n, i, arr === nums)); // true

// Holes: forEach skips them, map preserves them
const sparse = [1, , 3];       // length 3, index 1 is a hole
sparse.forEach((n) => console.log(n));      // 1, then 3
console.log(sparse.map((n) => n * 2));      // [2, <1 empty item>, 6]

If you need holes treated as undefined, normalize first: Array.from(sparse) produces [1, undefined, 3].

Mutator, accessor, and copy semantics

The methods split into three behavioral families, and confusing them is the most common source of bugs in modern codebases.

FamilyMutates?ExamplesReturn value
Mutatorsyes`push`, `pop`, `shift`, `splice`, `sort`, `reverse`, `fill`, `copyWithin`usually the array itself or a removed item
Accessorsno`slice`, `concat`, `join`, `indexOf`, `flat`a new value or array
Iterationno`map`, `filter`, `reduce`, `find`, `some`, `every`, `flatMap`new array or scalar

sort deserves special attention: its default comparator coerces elements to strings, so [10, 1, 100].sort() yields [1, 10, 100] by lexicographic order but [1, 10, 2].sort() yields [1, 10, 2] — while [2, 10, 1].sort() yields [1, 10, 2]. To sort numbers you must pass a comparator: (a, b) => a - b. The comparator must be consistent: return a negative number, zero, or positive number, and always the same answer for the same pair. Inconsistent comparators produce implementation-defined order. ES2019+ guarantees sort is stable, so equal elements keep their relative order.

Code example in javascript
const people = [
  { name: 'Ada', age: 36 },
  { name: 'Bo', age: 29 },
  { name: 'Cy', age: 36 },
];
people.sort((a, b) => a.age - b.age || a.name.localeCompare(b.name));
// stable tie-break by name within equal ages

Choosing the right method

Treat method selection as a decision, not a habit. The wrong choice is usually a performance or clarity bug.

  • find stops at the first match; filter(...)[0] scans everything and allocates. Prefer find.
  • some/every short-circuit; filter(...).length > 0 does not.
  • reduce can express almost anything, which is exactly why it should be used sparingly. A reduce that builds an object grouped by key is idiomatic; a reduce that reimplements map is not.
  • forEach cannot short-circuit and cannot be awaited meaningfully (see below). Use for...of when you need break, continue, or await.

Iteration methods, generators, and await

Iteration methods are synchronous. arr.map(async (x) => await fetch(x)) returns an array of promises, not awaited results. The common fixes:

Code example in javascript
const urls = ['/a', '/b', '/c'];

// Sequential: each starts after the previous resolves
for (const url of urls) {
  const res = await fetch(url);
  console.log(url, res.status);
}

// Concurrent: start all, then await all
const results = await Promise.all(urls.map((u) => fetch(u)));

Promise.all fails fast on the first rejection; Promise.allSettled waits for every promise and reports each outcome. forEach with an async callback is almost always wrong: it fires all callbacks concurrently and discards their promises, so errors become unhandled rejections.

Typed arrays and array-likes

NodeList, arguments, and HTMLCollection are array-like: they have length and indices but no array methods. Convert once at the boundary: Array.from(nodeList) or, if no transformation is needed, [...nodeList]. Typed arrays (Int32Array etc.) have most iteration methods but a fixed length and homogeneous numeric elements; push and pop do not exist on them.

Copy methods and frozen state

ES2023 added non-mutating counterparts: toSorted, toReversed, toSpliced, and with. Prefer them whenever the source array is shared state — a Redux store, a React prop, a module-level constant. Note that these are shallow copies: arr.with(0, {...obj}) copies the array, not the objects inside it. For genuinely immutable structures you need to copy the mutated elements too, or reach for a persistent-data-structure library.

Edge cases worth memorizing

  • new Array(3) creates a sparse array with three holes; [3] creates one element. Never use the Array(len) form to pre-fill — use Array.from({ length: n }, (_, i) => ...).
  • [10, 1, 3].sort((a, b) => a - b) is O(n log n) worst case; for already-nearly-sorted data a manual insertion pass can beat it, but profile before optimizing.
  • indexOf uses strict equality and cannot find NaN. Use includes for NaN and findIndex with a predicate for objects.
  • reduce without an initial value uses element 0 as the accumulator and throws on an empty array. Always pass an initial value unless the non-empty invariant is enforced upstream.

Exercise

What does [1, 2, 3].map(parseInt) return, and why?

Show answer

[1, NaN, NaN]. map passes (element, index, array), so parseInt receives the index as its radix argument: parseInt('1', 0) is 1 (radix 0 is treated as absent), parseInt('2', 1) is NaN, and parseInt('3', 2) is NaN.

Exercise

You need the first user over age 30 from a 100,000-element array. Why is find preferable to filter, and when would filter still be right?

Show answer

find short-circuits at the first match and allocates nothing; filter scans all 100,000 elements and allocates a result array. filter is right when you genuinely need all matches, or when you will reuse the filtered array multiple times so the single allocation pays for itself.

Exercise

Rewrite const sorted = [...arr].sort(cmp) using a single method, and state one behavioral difference.

Show answer

const sorted = arr.toSorted(cmp). Both produce a new sorted array, but toSorted also converts holes to undefined during the copy, whereas [...arr] preserves holes — so the two can differ on sparse arrays.

Spotted a mistake, or code that does not work?

Report an issue

Chapter: Arrays and Iteration Methods

0 / 2,000 characters

Plain text, for example: the second code sample does not compile. Do not include personal information.

You can't do that