_.set(obj, 'a.b.c', value) writes a value at an arbitrarily deep path, creating intermediate objects along the way. It's the natural companion to _.get() — and it's harder because you need to mutate rather than just read, and create missing nodes rather than returning a default.
What is _.set()?
set(obj, path, value) writes value at the location described by path, creating any intermediate objects (or arrays) that don't yet exist:
The hard part is determining what to create at each intermediate step: a plain object {} or an array []. The heuristic: if the next key in the path is a valid array index (a non-negative integer), create an array; otherwise create a plain object.
Real-world use cases:
- Form state — update
form.address.citywithout spread-cloning every layer - Config patching — set a deeply nested config value from a flat key path
- Redux reducers — safely write to nested state paths
- Template engines — populate nested data structures from dot-notation keys
The Problem
"Implement set(obj, path, value) that writes a value at the nested path. Path can be a dot-notation string like 'a.b.c', bracket notation like 'a[0].b', or an array of keys. Create intermediate objects/arrays as needed."
Thought Process
Two phases:
- Parse the path — normalize
'a[0].b'to['a', '0', 'b'](always an array of string keys) - Walk and write — traverse the object, creating missing nodes, then set the final key
For step 2: iterate over all keys except the last. At each key, check if the next node exists and is an object. If not, create it — using {} or [] based on whether the next key looks like an array index (/^\d+$/).
Step 1 — Parsing the Path
Loading editor...
Step 2 — Walk and Write
Loading editor...
Step 3 — Overwriting Non-Object Nodes
What if an intermediate node exists but is a primitive? It gets replaced.
Loading editor...
Full Solution
Loading editor...
What Interviewers Are Testing
- Path normalization — converting bracket notation to dot notation before splitting
{} vs []creation heuristic — using the next key's value to decide what to create: number → array, string → object- Primitive overwrite — not assuming existing nodes are objects; replacing them if they're not
- Mutates in place —
setmodifies the original object and returns it (Lodash behavior)
Complexity
| Time | Space | |
|---|---|---|
set(obj, path, val) | O(D) — D = path depth | O(D) — path keys array |
Interview Tips
- Handle bracket notation first —
.replace(/\[(\d+)\]/g, '.$1')before.split('.')is the cleanest path normalization. - Explain the
{} vs []decision — "I check whether the next key is a digit. If so, I create an array because integer keys indicate array indexing." - Mention immutability — "This mutates the object. For immutable set, you'd create new objects at each level — similar to how Redux reducers work with spread operators."
- Compare to
_.get— "Get traverses and returns; set traverses, creates, and writes. The traversal logic is the same, the write step is new."