AniUI Academy

Update a nested value immutably

hardArrays & Objects+350 XPPractice

Updating nested state immutably by hand produces a pyramid of spread operators, and getting it wrong either mutates state or copies everything, defeating the reference checks that React.memo and useMemo rely on.

Implement updateIn(source, path, updater). path is an array of segments: strings are object keys, numbers are array indices. Walk to the end of the path, call updater with the value found there, and return a new structure in which that position holds the returned value. Copy only the containers along the path: any branch you did not walk into keeps its original reference, so result.untouched === source.untouched. Arrays stay arrays and objects stay objects. If a segment along the path is missing, create the container the *next* segment needs — an array when that segment is a number, a plain object otherwise — and call updater with undefined at the end. An empty path means the whole value: return updater(source). The input is never modified.

Arguments

sourceany
The root value to walk into. Not modified.
pathArray<string | number>
Segments to walk. Strings are object keys; numbers are array indices.
updater(current) => any
Called with the value at the end of the path. Its return value is stored at that position.

Returns

any — A new structure identical to source except at the path, which holds updater's return value. Untouched branches keep their original references.

Examples

updateIn({ a: { b: { c: 1 } } }, ["a", "b", "c"], (n) => n + 1);
// { a: { b: { c: 2 } } }
updateIn({ list: [1, 2, 3] }, ["list", 1], (n) => n * 10);
// { list: [1, 20, 3] } — number segments keep the array an array

updateIn({}, ["xs", 0], () => "first");
// { xs: ["first"] } — missing containers are created based on the next segment

What it has to do

  • Treat string segments as object keys and number segments as array indices.
  • Return a new structure with the value at the path replaced by updater's return value.
  • Share references for every branch not on the path.
  • Keep arrays as arrays, and create missing containers based on the next segment's type.
  • Return updater(source) for an empty path, and never mutate the input.

Stuck? The javascript course covers everything this challenge needs.

Try it yourself
Loading playground...
5 tests · +350 XP

Discussion

Sign in to post
Sign in to join the conversation.Sign in

No comments yet. Be the first to say something.