Arrays & Collections
Array methods and mutation, sort and copy semantics, Map and Set, WeakMap and WeakSet, typed arrays, and sparse arrays.
13 questions
JuniorTheoryVery commonWhat do the array methods map, filter, reduce, and forEach each do?
What do the array methods map, filter, reduce, and forEach each do?
map returns a new array of the same length by transforming each element. filter returns a new array keeping only elements whose callback returns truthy. reduce folds all elements into a single accumulated value via a reducer and a seed. forEach just runs a callback for each element and returns undefined — it is used for side effects, not for building a result.
Common mistakes
- ✗Using
mapfor side effects whenforEachis meant —mapthen builds an array ofundefined - ✗Expecting
forEachto return a value or be chainable likemap/filter - ✗Forgetting
reduceneeds a seed to safely handle an empty array
Follow-up questions
- →Why does
forEachignore a value youreturnfrom its callback? - →How would you express both
mapandfilterusing onlyreduce?
JuniorTheoryVery commonWhat is the difference between the array slice and splice methods?
What is the difference between the array slice and splice methods?
slice(start, end) is non-mutating: it returns a new shallow copy of the selected range and leaves the original array untouched. splice(start, deleteCount, ...items) mutates the original in place — it removes, inserts, or replaces elements there — and returns an array of the removed elements. So slice is for reading a subset, splice is for editing the array itself.
Common mistakes
- ✗Confusing the two and mutating an array with
splicewhen a copy viaslicewas intended - ✗Thinking both return the same thing —
splicereturns removed elements,slicereturns the copied subset - ✗Forgetting
splicecan also insert/replace elements, not only remove them
Follow-up questions
- →How do you use
spliceto insert elements without removing any? - →Why is the copy returned by
slicecalled shallow?
JuniorTheoryCommonHow does Array.prototype.sort order elements by default, and how do you sort numbers correctly?
How does Array.prototype.sort order elements by default, and how do you sort numbers correctly?
By default sort converts each element to a string and compares them by UTF-16 code units, so [10, 2, 1] becomes [1, 10, 2] — lexicographic, not numeric. To sort numbers correctly you pass a compareFn (a, b) => a - b: a negative result puts a first, positive puts b first, zero keeps order. sort also mutates the array in place and returns it.
Common mistakes
- ✗Sorting numbers without a compareFn and getting lexicographic order like
[1, 10, 2] - ✗Returning a boolean from the compareFn instead of a negative/zero/positive number
- ✗Assuming
sortreturns a new array — it mutates the original in place
Follow-up questions
- →What does the compareFn return value's sign mean for the order of two elements?
- →How would you sort descending or sort an array of objects by a field?
MiddleTheoryCommonWhat do the array iteration methods some, every, find, findIndex, and reduce each return?
What do the array iteration methods some, every, find, findIndex, and reduce each return?
some returns true if at least one element passes the predicate; every returns true only if all do (and true for an empty array). find returns the first element that passes, or undefined; findIndex returns its index, or -1. reduce folds the array into one accumulated value. some, every, and find short-circuit as soon as the result is decided.
Common mistakes
- ✗Swapping
someandevery—someis any-match,everyis all-match - ✗Expecting
findto return an index or a boolean instead of the element - ✗Assuming
everyon an empty array isfalse— it returnstruevacuously
Follow-up questions
- →Why does
everyreturntruefor an empty array whilesomereturnsfalse? - →How does short-circuiting in
some/findaffect performance on large arrays?
MiddleTheoryCommonHow does a Map differ from a plain object, and how does it differ from a Set?
How does a Map differ from a plain object, and how does it differ from a Set?
Versus a plain object, a Map accepts any value as a key (not just strings and symbols), guarantees insertion order, exposes size, is directly iterable, and has no prototype keys to collide with. An object is simpler, JSON-serializable, and fine for fixed string keys. Versus a Set: a Map stores key→value pairs, while a Set stores only unique standalone values with no associated value — so Set is for membership tests, Map for lookups.
Common mistakes
- ✗Using an object where non-string keys are needed — only
Mapsupports arbitrary keys - ✗Forgetting object keys can collide with inherited prototype names like
toString - ✗Thinking
Setstores key→value pairs — it stores only standalone unique values
Follow-up questions
- →When is a plain object still the better choice over a
Map? - →How would you convert a
Mapto a plain object and back?
MiddleTheoryCommonWhich array methods mutate the original array, and which return a new one instead?
Which array methods mutate the original array, and which return a new one instead?
Mutating methods change the array in place: push, pop, shift, unshift, splice, sort, reverse, and fill. Non-mutating methods return a new array and leave the original untouched: map, filter, slice, concat, and the ES2023 copying methods toSorted, toReversed, toSpliced, and with. Mutating methods often return something else — push returns the new length, pop the removed element — while non-mutating ones return the new array.
Common mistakes
- ✗Assuming
sortandreversereturn a copy — they mutate in place and return the same array - ✗Forgetting
push/popreturn a length/element, not the array, so they don't chain likemap - ✗Confusing
slice(copy) withsplice(mutate)
Follow-up questions
- →How do the ES2023
toSorted/toReversedmethods relate tosort/reverse? - →Why can a mutating method like
sortcause bugs in code that shares an array reference?
SeniorTheoryCommonWhat patterns let you transform an array without mutating the original?
What patterns let you transform an array without mutating the original?
Prefer methods that return a new array: map, filter, slice, concat, and spread [...arr]. The ES2023 copying counterparts of the mutators — toSorted, toReversed, toSpliced, and with — replace sort/reverse/splice and index assignment without touching the source. reduce builds a derived structure in one pass. When only a mutator exists, copy first then mutate the copy: const sorted = [...arr].sort(cmp). These patterns matter for immutable state in frameworks like React.
Common mistakes
- ✗Calling
sort/reverseon shared state, mutating it under other holders of the reference - ✗Thinking spread
[...arr]deep-clones — it is a shallow copy - ✗Reaching for
JSON.parse(JSON.stringify(...))whenmap/slice/toSortedsuffice
Follow-up questions
- →Why does immutable state matter for change detection in frameworks like React?
- →How would you immutably update one element at a given index without
with?
JuniorTheoryOccasionalWhat is a Map, and what makes it different from a plain object for storing key–value pairs?
What is a Map, and what makes it different from a plain object for storing key–value pairs?
Map is a built-in keyed collection where keys can be any value — objects and functions, not just strings and symbols. It remembers insertion order, exposes a size property, and is directly iterable with for...of, .keys(), .values(), and .entries(). You read and write with get, set, has, and delete. It has no prototype keys, so user keys can never collide with inherited properties.
Common mistakes
- ✗Believing
Mapkeys are coerced to strings like object keys — any value works as a key - ✗Using
map.lengthinstead of thesizeproperty to count entries - ✗Forgetting
Mappreserves insertion order during iteration
Follow-up questions
- →How does a
Mapcompare object keys — by reference or by structural contents? - →Why can't you serialize a
Mapdirectly withJSON.stringify?
JuniorTheoryOccasionalWhat is a Set, and how do you use it to remove duplicates from an array?
What is a Set, and how do you use it to remove duplicates from an array?
Set is a built-in collection of unique values: adding a value already present is ignored, so it never holds duplicates. Uniqueness uses SameValueZero, so NaN is treated as one value. It is iterable in insertion order with a size property. To dedupe an array, wrap it in a Set and spread back: [...new Set(arr)], which keeps the first occurrence of each value.
Common mistakes
- ✗Thinking
Setuses==for uniqueness — it uses SameValueZero, so1and'1'stay distinct - ✗Believing
NaNcan appear twice in aSet— it counts as one value - ✗Forgetting to spread the
Setback into an array when an array result is needed
Follow-up questions
- →How does
Setdecide two values are equal — what is SameValueZero? - →Why does
[...new Set(arr)]keep the first occurrence of each duplicate?
SeniorTheoryOccasionalWhat subtle guarantees govern Array.prototype.sort — stability, mutation, comparator, holes?
What subtle guarantees govern Array.prototype.sort — stability, mutation, comparator, holes?
Default sort is lexicographic (elements stringified, compared by UTF-16), so numbers need (a, b) => a - b. Since ES2019 sort is guaranteed stable: equal elements keep their relative order. It mutates the array in place and returns the same reference. The comparator must be consistent — returning negative/zero/positive purely from its two arguments — or the order is implementation-defined. undefined values and holes are never passed to it and always move to the end.
Common mistakes
- ✗Believing
sortis unstable — it has been guaranteed stable since ES2019 - ✗Returning a boolean from the comparator instead of a signed number
- ✗Assuming
undefined/holes are sorted by the comparator — they go to the end untouched
Follow-up questions
- →Why does an inconsistent comparator (e.g. random return) produce an undefined ordering?
- →How would you sort without mutating the source — what method or copy do you use?
MiddleTheoryRareWhat is the difference between dense and sparse arrays, and how do iteration methods treat holes?
What is the difference between dense and sparse arrays, and how do iteration methods treat holes?
A dense array has a value at every index from 0 to length - 1; a sparse array has holes — missing indices, created by [1, , 3], Array(3), delete arr[i], or raising length. A hole is not the same as an element set to undefined. Methods like forEach, map, filter, and reduce skip holes entirely (the callback never runs there), and map preserves the holes in its result. Reading a hole yields undefined, so in or hasOwnProperty is the reliable way to detect one.
Common mistakes
- ✗Treating a hole as identical to an element equal to
undefined - ✗Expecting
forEach/mapto invoke the callback at every index in a sparse array - ✗Believing
mapcollapses holes — it actually preserves them in the result
Follow-up questions
- →How can you distinguish a hole from a real
undefinedelement at an index? - →Which newer methods like
Array.fromor spread treat holes asundefinedinstead of skipping them?
MiddleTheoryRareWhat are typed arrays, and how do ArrayBuffer and views like Uint8Array work together?
What are typed arrays, and how do ArrayBuffer and views like Uint8Array work together?
An ArrayBuffer is a fixed-length block of raw binary bytes you cannot read directly. A typed array like Uint8Array or Float64Array is a view that interprets those bytes as numbers of a fixed type and width. Multiple views can share one buffer, so writing through one view changes the bytes the other sees. Typed arrays have a fixed length and only hold numbers — they back binary data such as files, network frames, and WebGL/canvas pixels.
Common mistakes
- ✗Thinking an
ArrayBuffercan be indexed directly — you need a view over it - ✗Believing views over the same buffer are independent — they share the bytes
- ✗Expecting typed arrays to grow or hold non-numeric values like a normal array
Follow-up questions
- →How does a
DataViewdiffer from a typed-array view over the same buffer? - →What does endianness mean when you read a multi-byte number from a buffer?
MiddleTheoryRareWhat are WeakMap and WeakSet, and how do they differ from Map and Set?
What are WeakMap and WeakSet, and how do they differ from Map and Set?
WeakMap keys and WeakSet members must be objects (or symbols), and they are held weakly: if nothing else references the object, it can be garbage-collected and the entry disappears automatically. Because membership can vanish at any time, they are not iterable, have no size, and cannot be cleared by enumeration. They expose only get/set/has/delete (add/has/delete for WeakSet). Typical use: attaching private metadata to objects without leaking memory.
Common mistakes
- ✗Trying to use a primitive as a
WeakMapkey — keys must be objects or symbols - ✗Expecting to iterate a
WeakMap/WeakSetor read itssize - ✗Thinking weak references keep an object alive — they do the opposite
Follow-up questions
- →Why does the inability to iterate follow from weak references being non-deterministic?
- →How does a
WeakMaphelp implement truly private per-instance data?