Start by locating the data and deciding what can be missing. Every later transformation depends on this.
Write it in this order
Identify whether the input is an array, object, or nested combination.
Read the needed property or turn an object into keys, values, or pairs.
Guard a possibly missing array item with ?. and choose a fallback with ?? only if needed.
Remember
Object.entries gives [key, value] pairs. Optional chaining handles null or undefined, not every possible runtime error.
Watch for
An object containing timezone: undefined still has that key. Object.keys checks own enumerable string keys; an “empty” result does not describe a Map or Set.
TRY FROM MEMORY
Read team index 9 safely and test whether settings is empty.
A loop makes the accumulator, condition, and stopping rule explicit. Array methods are easier once you can write their loop version.
Write it in this order
Initialize the result with its type: an array, a counter, or Item | undefined.
Iterate the items with for…of; use entries() when you also need the index.
Update the result, and break as soon as a first-match task is solved.
Remember
for…of gives values; for…in gives string keys and may include inherited enumerable properties. Object.entries is the usual choice for own object pairs.
Watch for
Do not use for…in to get array items. An indexed lookup can be undefined; guard it. A first-match result must allow “not found.”
TRY FROM MEMORY
Find the oldest user and the first inactive user with loops.
Check: Cara; Ben
TypeScript implementation Open code +
Includes sample data when copied
const loudNames: string[] = [];
for (const user of users) {
loudNames.push(user.name.toUpperCase());
}
let oldestLoop: User | undefined;
for (let i = 0; i < users.length; i++) {
const user = users[i];
if (user && (!oldestLoop || user.age > oldestLoop.age)) {
oldestLoop = user;
}
}
let firstInactive: User | undefined;
for (const user of users) {
if (!user.active) {
firstInactive = user;
break;
}
}
for (const [index, user] of users.entries()) {
console.log(index, user.name);
}
for (const [key, value] of Object.entries(settings)) {
if (value !== undefined) console.log(key, value);
}
const settingLines: string[] = [];
for (const [key, value] of Object.entries(settings)) {
settingLines.push(key + ": " + String(value));
}
const keyList: string[] = [];
for (const key in settings) keyList.push(key);
03
FOUNDATIONS
Choose the output, then the method
map · filter · find · some · every
Choose by the return shape: transformed array, retained items, one item, or a boolean.
Write it in this order
Write down the output type before choosing a method.
Use map for one output per item, filter for a subset, find for one match, or some/every for a boolean.
Write the smallest callback that returns exactly what that method needs.
Remember
map returns a new value; filter/find/some/every return a test from their callback. filter preserves the original item references.
Watch for
An arrow callback with { braces } needs an explicit return. On an empty array: find → undefined, some → false, every → true.
TRY FROM MEMORY
Get the names, active users, first user under 30, and whether everyone is active.
Grouping is the same accumulation pattern as a sum, with a collection as the result.
Write it in this order
Choose the accumulator type and give reduce an initial value.
For a total, add one value; for a group, calculate its key and obtain or create its bucket.
Update the accumulator and return it on every callback call.
Remember
Use 0 for sums and {} or a Map for groups. A local accumulator created for this result can be mutated without mutating the input.
Watch for
reduce without an initial value throws on empty input. Object keys are strings at runtime, even for numeric ages. Prefer Map for arbitrary keys such as user-supplied names.
Use the same transform/filter ideas on key-value pairs. Build the id lookup now because the later join depends on it.
Write it in this order
For a patch, spread the object then write the override; for removal, destructure the unwanted key and keep ...rest.
For a transformation, get entries, map or filter the pairs, then call Object.fromEntries.
To build an index, map each user to [id, user], then call Object.fromEntries.
Remember
The pair is the unit of object transformation. Keep the key when changing a value. Explicitly test value !== undefined to preserve 0, false, and empty strings.
Watch for
Later properties win. Setting a value to undefined does not remove its key. Duplicate ids in fromEntries keep the last value; a missing lookup still needs a guard.
TRY FROM MEMORY
Set fontSize to 18, remove timezone, drop undefined values, and index users by id.
Check: 18; timezone key absent; defined settings; usersById[3] is Cara
A nested update is several flat updates composed along one path.
Write it in this order
Locate the changed leaf, such as company.address.city.
Write the replacement from the inside out: copy address and override city, then copy company and replace address.
Check that changed ancestors are new references and untouched branches can still be shared.
Remember
Spread copies one level. For cloneable data, structuredClone creates a deep copy that you can then mutate independently.
Watch for
A top-level spread still shares address and teams. structuredClone copies the whole graph, loses useful sharing, and cannot clone values such as functions or DOM nodes.
TRY FROM MEMORY
Move the company to Denver without changing the original Boston address.
Check: moved !== company; moved.address !== company.address; moved.teams === company.teams
No new primitive is needed: reuse the array trio inside the copied object path.
Write it in this order
Write the outer {...company, teams: …} shell, then map the teams to locate the target id.
Return nonmatching teams unchanged. For the match, spread the team and replace the changed property.
For memberIds, add with a spread or remove with filter; then verify both values and reference identity.
Remember
For company → teams → team → memberIds, copy every container on that path. An unchanged team should keep its reference.
Watch for
Copying the team but pushing into its existing memberIds still mutates the original. Specify what should happen when a target id is missing or a member is already present.
TRY FROM MEMORY
Rename Backend to Platform; add member 5 to Frontend; remove member 3 from Backend.
Check: Platform; [1, 2, 5]; [4] — each in its own result
Finish by composing the earlier operations in dependency order. Keep each intermediate shape clear.
Write it in this order
Build usersById before resolving member ids. Read the selected team with ?. and default missing memberIds to [].
flatMap each id to [user] when found or [] when missing; this both joins and removes missing partners.
For active names: filter while active is still available, map to name, then sort the resulting strings.
Remember
Pipeline order follows the data you still need. After mapping users to names, you no longer have the active property for filtering.
Watch for
A find inside every member loop costs O(mn); a lookup built once makes the join expected O(n + m). Sorting the new mapped array is safe for the original users array.
TRY FROM MEMORY
Resolve Frontend’s members and return alphabetized active names.
Check: [Ana, Ben]; [Ana, Cara]
TypeScript implementation Open code +
Includes sample data when copied
// Build this first — the join depends on it (stage 07).
const usersById = Object.fromEntries(users.map(u => [u.id, u]));
const frontendMembers = (company.teams[0]?.memberIds ?? []).flatMap(id => {
const user = usersById[id];
return user ? [user] : [];
});
const activeNames = users
.filter(u => u.active)
.map(u => u.name)
.sort((a, b) => a.localeCompare(b));
THE FINAL CHECK
Can you combine the moves without looking?
Rename a team, add a member, resolve its member ids to users, keep only active users, and return their alphabetized names. First name the intermediate shape after each step. Then implement it.
n = users; k = object keys; t = teams; m = member ids; a = active users. String comparisons also depend on string length. Hash lookups use expected-time shorthand; sort performance depends on the engine.