Arrays, objects, and the methods people actually use
map, filter and reduce over a hand-written loop, and the two ways to copy an object.
After this lesson you can
- Choose between map, filter and reduce without reaching for a loop first
- Destructure an object and an array, including with defaults
- Explain what spread copies and what it does not
Three methods cover most of what a hand-written for loop over an array
is doing.
const nums = [1, 2, 3, 4];
nums.map((n) => n * 2); // [2, 4, 6, 8] — same length, transformed
nums.filter((n) => n % 2 === 0); // [2, 4] — a subset, same shape
nums.reduce((sum, n) => sum + n, 0); // 10 — collapsed to one value
map transforms every element and keeps the length. filter keeps
some elements and drops the rest. reduce folds the whole array into
one value — a sum, an object, another array, anything. Reaching for
reduce to build a new array is legal and occasionally the cleanest
option, but map/filter read better when either one alone would do.
None of the three mutate the original array. push, pop, splice and
sort do — sort in particular surprises people, because it sorts in
place and returns the same array, so const sorted = nums.sort()
quietly reorders nums too.
Destructuring
Pulling values out of an object or array by shape, instead of by
obj.property one line at a time.
const { name, email = "unknown" } = user; // email defaults if absent
const [first, , third] = [1, 2, 3]; // skip an element with a comma
const { address: { city } = {} } = user; // nested, with a fallback object
The nested default (= {}) matters: without it, a user with no
address throws trying to read .city off undefined, rather than
quietly giving city the value undefined.
Try it
export function run() { const words = ["pear", "plum", "kiwi", "fig", "date"]; return words.reduce( (acc, w) => ({ shortest: w.length < acc.shortest.length ? w : acc.shortest, longest: w.length > acc.longest.length ? w : acc.longest, letters: acc.letters + w.length, }), { shortest: words[0], longest: words[0], letters: 0 }, );}Spread copies one level deep
const original = { name: "Nino", address: { city: "Tbilisi" } };
const copy = { ...original, name: "Ana" };
copy.address.city = "Batumi";
original.address.city; // "Batumi" — the same nested object, not a copy
{ ...original } creates a new top-level object, but every value that
was itself an object is copied by reference, not cloned. This is a
shallow copy, and it is the correct default — a genuine deep copy is
rarely what you actually want, and it is easy to reach for
structuredClone(value) on the rare occasion it is. Lesson 11 goes
further into copying.
export function run() { const nums = [3, 1, 2]; const sorted = nums.sort((a, b) => a - b); let emptyReduce; try { emptyReduce = [].reduce((a, b) => a + b); } catch (e) { emptyReduce = `${e.name}: ${e.message}`; } const original = { name: "Nino", address: { city: "Tbilisi" } }; const copy = { ...original, name: "Ana" }; copy.address.city = "Batumi"; return { nums, sortedIsSameArray: sorted === nums, emptyReduce, originalName: original.name, originalCity: original.address.city, };}Common mistakes
const sorted = items.sort(...)on data you do not own. The caller's array is reordered too, andsorted === items— the example shows it. Copy first ([...items].sort(...)) or usetoSorted.reducewith no initial value. On an empty array it throwsTypeError: Reduce of empty array with no initial value; always pass the starting value.- Editing a spread copy's nested object. The original changes with it, because only the top level was copied.
- Nested destructuring without a fallback.
const { address: { city } } = userthrows whenaddressis missing; add= {}.
What interviewers ask
- What is the difference between
mapandforEach? —mapreturns a new array of the callback's results;forEachreturnsundefinedand exists for side effects. - Does
const arr = []make the array immutable? — No. stops the name being reassigned; the array can still be pushed to and sorted.
Try it yourself
2 visible tests · 2 hidden testsImplement cheapestTwo(products). Each product is
{ name, price }. Return the name of the two cheapest products,
cheapest first, as an array of two strings. Do not mutate the input
array. Assume there are always at least two products and no ties.
run is given and is what the tests call: it returns
{ cheapest, inputAfter }, where cheapest is your result and
inputAfter lists the input's names in their order after your
function ran — so a sort that reorders the caller's array shows up.
run([{"name":"Notebook","price":3.5},{"name":"Pen","price":1.2},{"name":"Desk lamp","price":24}])run([{"name":"A","price":1},{"name":"B","price":2}])
Sign up to run the tests, check the hidden ones and save your progress. Sign up