Length is not presence
New Array(3) creates an array of length three with holes. In an environment without inherited numeric properties, map does not call its callback for those positions. An array [undefined, undefined, undefined] is different: it has three present elements even though their values are undefined.
Map supplies value, index and array to its callback. Passing parseInt directly causes the index to be interpreted as radix: intending to convert each decimal string does not define a correct call. Use a function with explicit arguments, such as value => parseInt(value, 10), when prefix parsing really is the contract; for complete text validation, apply boundary rules first.
Iterated values and shared objects
A for...of loop consumes values from an iterator. With the normal Array iterator, an extra note property does not become a sequence element. Do not confuse this iteration with enumerating property names. In this lesson’s analyses, arrays are not mutated during iteration.
Fill receives one value and places it at selected indices. If that value is an object, all those positions receive the same reference. Array.from({ length: 2 }, () => ({ count: 0 })) calls a function that creates an object per position. Subsequently copying the array created by fill does not separate counters that are already shared.
Ordering and effects on input
Sort mutates its receiver and returns that same array. Without a comparator, numbers are compared through their text representations: [4, 21, 3] becomes [21, 3, 4]. For small validated integers, (a, b) => a - b establishes ascending numeric order. A comparator returning only a > b does not correctly express negative, zero and positive outcomes.
ToSorted produces a new array. You can also copy a dense numeric array first and sort the copy. Neither operation recursively duplicates object elements. Stable sorting preserves received order when the comparator considers two elements equivalent; it does not invent a secondary ID criterion.
Accumulators and empty collections
Reduce without an initial value seeks a first element to use as the accumulator. An empty array has none, so it throws TypeError. For summing scores, an explicit 0 defines the empty result and supports the same contract for any record count. Choose the initial value according to the operation, not merely to silence the exception.
Retain the declared numeric bounds: in the example, up to 1000 integer scores from 0 to 100 yield a maximum total of 100000, inside the safe Number range. This argument does not authorize using the same unbounded sum for money or arbitrarily large integers. Also distinguish no records from failure to read the records.
Identity in Map, Set and lookup
Map distinguishes Number key 9 from string key "9". The import contract must decide the type before insertion or lookup. If "09" and "9" are different identities, converting both to Number destroys information. Updating an existing key with set changes its value without moving the entry to the end.
Set compares objects by identity rather than their fields. Two separately created { id: "K" } objects remain two members. Equality rules also vary between methods: includes finds NaN through SameValueZero; indexOf uses strict equality and does not find it. Select the rule matching the data meaning instead of assuming a universal structural comparison.
Example: stable independent report
BuildReport first projects each validated record into a new object with id and score. Subsequent sorting mutates only the snapshot array. The comparator uses score; stable ties retain relative input position. For the displayed input, the order is C, B, A and the total is 9.
The later write input[0].score = 99 changes neither report.rows nor report.total. If we used only input.toSorted, the outer array would be new, but B would still reference the edited object and could cease to match the retained total. Projection suits this two-primitive-field schema; it is not a generic solution for every object graph.
Decisions and acceptance criteria
Review a transformation in stages: confirm data format and bounds, key identity, each callback’s arguments and the references that remain shared. Then determine which objects are mutated. Only then compare ordering and totals with requirements.
Include at least an empty array, a tie and two objects with equal fields but distinct identities in the analysis. When a historical snapshot is required, add a later mutation to source objects. These situations distinguish a transformation that appears correct for a simple list from one preserving the complete contract. Assessments request choices justified by this reasoning without code execution.
function buildReport(rows) {
const snapshot = rows.map(({ id, score }) => ({ id, score }));
snapshot.sort((a, b) => a.score - b.score);
const total = snapshot.reduce((sum, row) => sum + row.score, 0);
return { rows: snapshot, total };
}
const input = [{ id: "B", score: 4 }, { id: "A", score: 4 }, { id: "C", score: 1 }];
const report = buildReport(input);
input[0].score = 99;Assume a dense array of at most 1000 ordinary records, with string id and integer score from 0 to 100. Predict report order and total, including ties and empty input. The final input mutation must not rewrite the report.
Common pitfalls
Using map to fill holes; sorting numbers without a comparator; passing parseInt directly to map; assuming Set compares fields; copying only the outer array.
Related topics: Values, conversions and numeric contracts · Objects, identity and property boundaries · State, ordering and integration diagnosis
State the identity rule, ordering and permitted effects. The chosen method must satisfy all three, including empty inputs and ties.
Reference: Array.prototype.map · ECMAScript 2026 (17th edition); browser APIs identified separately