Kata: get by path
Guard each read with == null; give the fallback only for undefined.
Predict first
Before you read: predict
ExamHall's form builder reads section settings with the team's kata answer:
In one form, form.sections[2] is { timeLimit: 0, instructions: '', questions: [] }: an untimed section with no instructions and no questions yet. The builder makes three calls: (1) get(form, 'sections[2].instructions.length', -1), (2) get(form, 'sections[2].timeLimit', 30), (3) get(form, 'sections[2].questions.length', -1).
Which calls return their fallback?
export function get(obj, path, fallback) { const keys = Array.isArray(path) ? path : String(path).match(/[^.[\]]+/g) ?? []; let cur = obj; for (const k of keys) { if (!cur) return fallback; cur = cur[k]; } return cur === undefined ? fallback : cur;}The 30-second version
get(obj, path, fallback) turns the path into keys and reads them one at a time. Before each read it returns the fallback if the current value is null or undefined. At the end it swaps in the fallback only for undefined, so 0, '', false and null come back.
Guard each read with == null; give the fallback only for undefined.
How it works
Step 1 of 13
null come back? Is the fallback costly?Analogy: A biobank technician's request sheet (clinical research)
A request sheet names a sample by its location, and the technician fetches it.
| At the biobank | In the kata |
|---|---|
| the line "freezer 3, rack 2, box 0, tube A1" read into separate steps | the path string turned into keys (step 3) |
| she opens each level in turn and stops at the first one that is not there | a guard before each read (step 6) |
| a tube holding 0 ml, or one with a blank label, is reported as found | 0 and '' come back (steps 2, 9) |
| a slot tagged "withdrawn" is reported as withdrawn | null comes back (step 9) |
| only an empty slot gets the substitute named at the foot of the sheet | the fallback, for undefined only (step 9) |
| a substitute thawed before she walks to the freezer is thawed whether or not it is needed | the fallback argument is computed on every call (step 11) |
In real terms: get is a loop of property reads with a == null stop before each read and an === undefined swap at the end.
Breaks down when: she can tell a slot never used from one emptied last week, while get gives the fallback for a missing key and for a key holding undefined alike (step 10).
Contrasting case: a technician who hands over the substitute whenever a tube looks empty, 0 ml included: that is || (step 9).
Examples, run for real
4.1 ExamHall EXH-3540: an untimed section with a 30-minute timer
The form builder read each section's timeLimit with a draft whose last line was return cur || fallback;, with 30 minutes as the fallback. A section saved as untimed (timeLimit: 0) opened with a 30-minute timer (EXH-3540). MR !3840 replaced the draft with the reference and added the test list to the repository. The list also runs on two more common mutants: M2 ends in return cur ?? fallback;, and M3 has no guard in the walk.
4.1 reference.mjs 10 of 10 pass
4.1 M1, return cur || fallback 7 of 10 pass · red: T6 "a resolved 0, '' or false comes back as it is", T7 "a resolved null comes back as null", T10 "reads a property of an empty string on the way (''.length is 0)"
4.1 M2, return cur ?? fallback 9 of 10 pass · red: T7 "a resolved null comes back as null"
4.1 M3, no guard in the walk 8 of 10 pass · red: T4 "a missing key on the way returns the fallback, or undefined with none given", T5 "a null on the way returns the fallback without throwing"
4.1 EXH-3540 the M1 draft, a section saved as untimed (timeLimit 0): timer set to 30 minutes
4.1 MR !3840 the reference, the same section: timer set to 0 (untimed)
M1 loses every falsy value the path finds, the 0 length of an empty string included (step 9). M2 loses only a stored null. M3 throws as soon as a key on the way is missing (step 6).
4.2 Payments: a key that holds a dot
A ledger entry keeps its card processor's metadata under keys such as 'psp.fee', as { meta: { 'psp.fee': 30 } }:
4.2 get(entry, 'meta.psp.fee', 0): 0, read as the keys meta, psp, fee
4.2 get(entry, ['meta', 'psp.fee'], 0): 30
The string path cannot say that a dot belongs to the key, so it reads meta, psp, fee and finds nothing; the array path reaches it (step 5).
Try it
Answer first, then check. Nothing is saved.
Question 1 of 8
A ledger service reads entries with this kata answer:
An entry is { lines: [{ amount: 1250 }, { amount: -40 }] }.
What does get(entry, 'lines[1].amount', 0) return?
export function get(obj, path, fallback) { const keys = Array.isArray(path) ? path : path.split('.'); let i = 0, cur = obj; while (cur != null && i < keys.length) cur = cur[keys[i++]]; return i < keys.length || cur === undefined ? fallback : cur;}Question 2 of 8
The ledger team writes the kata from its contract: walk the keys, return the fallback at a null or undefined met on the way, and give the fallback at the end only when the value found is undefined. The skeleton, with toKeys already written:
Put the six lines in order, the first line first.
In what order do the lines run?
export function get(obj, path, fallback) { const keys = toKeys(path); /* the six lines */}Your order
- 1empty
- 2empty
- 3empty
- 4empty
- 5empty
- 6empty
Tap the lines in the order you expect.
Question 3 of 8
A ledger report fills in each entry's exchange rate; currentRate() calls the rate service. get is the kata's reference answer:
The report runs on three entries: { fx: { rate: 1.1 } }, { fx: {} } and {}.
Which expression gives every entry a rate and calls the rate service only for the two entries with none stored?
import { get } from './get.mjs'; export const rates = (entries, currentRate) => entries.map((e) => /* the expression */);Question 4 of 8
The ledger keeps its fee rules in a JSON config, one rule per fee, such as { "field": "fees[0].amount", "fallback": 25 }. get is the kata's reference answer:
With that rule, a waived fee { fees: [{ amount: 0 }] } must give 0, an entry with no fees { fees: [] } must give 25, and { fees: [{ amount: 40 }] } must give 40.
Which expression does all three?
import { get } from './get.mjs'; export function feeFor(entry, rule) { return /* the expression */;}Question 5 of 8
the interviewer says: "Implement get(obj, path, fallback), like lodash's. The path is a string such as 'sections[2].timeLimit' or an array of keys. Return the fallback only when the value is undefined." You have 15 minutes and a plain editor, and ExamHall's form builder is the use case.
Before you type: say your test list out loud.
Name at least seven tests, each with its call and what it expects.
Question 6 of 8
the interviewer says: "Implement get(obj, path, fallback), like lodash's. The path is a string such as 'sections[2].timeLimit' or an array of keys. Return the fallback only when the value is undefined." You have 15 minutes and a plain editor, and ExamHall's form builder is the use case.
Now write it, in 15 minutes, from a blank file that exports get.
Question 7 of 8
A ledger screen labels each entry's settlement; get is the kata's reference answer:
The ledger writes settledAt: null for an entry that has not settled.
What is label?
import { get } from './get.mjs'; const entry = { id: 'tx-81', settledAt: null };export const label = get(entry, 'settledAt', 'pending') ?? 'not settled';Question 8 of 8
A candidate guards the walk with in:
A payout screen sizes a column with get(payout, 'currency.length', 0), and payout is { currency: 'EUR' }.
What does the call give?
export function get(obj, path, fallback) { const keys = Array.isArray(path) ? path : String(path).match(/[^.[\]]+/g) ?? []; let cur = obj; for (const k of keys) { if (cur == null || !(k in cur)) return fallback; cur = cur[k]; } return cur === undefined ? fallback : cur;}Go deeper
Pro tips
- Run your own answer against the list before you call it done:
KATA_IMPL=./my-get.mjs node --test kata.test.mjs. When: practising the kata at home, against the same tests the reference passes (step 13). Verify (RAN,tips.mjs):tip 1 KATA_IMPL=./reference.mjs: exit 0 · tests 10 · pass 10 · fail 0 tip 1 KATA_IMPL=<temp>/m1-get.mjs (return cur || fallback): exit 1 · tests 10 · pass 7 · fail 3 · red: T6, T7, T10 - Check your test list against lodash's own
getbefore the interview: lodash 4.17.21 passes all ten, so a test it fails is a wrong test. Know its one extra rule: it first tries the whole string as a key. When: writing the list at home, and when the interviewer asks how close you are to lodash (step 1). Verify (RAN,tips.mjs):tip 2 lodash 4.17.21 _.get on the kata's list: 10 of 10 pass tip 2 on { 'psp.fee': 30 }, path 'psp.fee': lodash 30 · reference undefined
Source
Vendor and guide pages change, so their claims are labelled as of 2026-10-12; every behaviour this lesson grades is executed on Node 24.
- Lodash 4.17.15 documentation,
_.get(the curriculum row's first source), https://lodash.com/docs/4.17.15#get. lodash.com timed out on 2026-10-12 (curl exit 28, twice), so the page was read from the unmodified Internet Archive capture (id_, Memento-Datetime 2025-12-05T10:14:29Z) of that URL, as_fix-brief-r1.mdruling 1 accepts for a named primary page (lodash-get.txt, header linesVia,Memento-DatetimeandRaw-SHA256). It says_.get"Gets the value at path of object", that "If the resolved value is undefined, the defaultValue is returned in its place", and lists the path argument as "(Array|string)" (steps 2, 3, 5). Lodash keys no item; this lesson's reference is its own code, and lodash 4.17.21 is run only bytips.mjs. - MDN, Optional chaining (?.) (the row's second source), https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining (
mdn-optional-chaining.txt): "If the object accessed or function called using this operator is undefined or null, the expression short circuits and evaluates to undefined instead of throwing an error", and "You can also use the optional chaining operator with bracket notation, which allows passing an expression as the property name" (step 12). As of 2026-10-12. - Front End Interview Handbook, JavaScript coding, https://www.frontendinterviewhandbook.com/coding/javascript-utility-function (
feih-javascript-coding.txt): candidates take "around 10-15 minutes for a basic question" (the time box, step 1), and it advises "writing test cases" (step 13). As of 2026-10-12.
Related lessons
Checked against the tools and docs as of 2026-10-12.