#621 Set

medium
javascript
objects

Reading a deeply nested value safely is only half the job. Form libraries, state reducers and config loaders also need to write into a nested path like "user.address.city" without first checking that user and address exist.

Write a function set(object, path, value) that assigns value at path inside object, creating any missing containers on the way. It mutates object and returns that same object.

const form = {};

set(form, "user.address.city", "Lisbon");
// { user: { address: { city: "Lisbon" } } }

set(form, "user.phones[0]", "555-0100");
// { user: { address: { city: "Lisbon" }, phones: ["555-0100"] } }

set(form, ["meta", "last.saved"], 1700000000);
// adds { meta: { "last.saved": 1700000000 } }

Rules

  • path is either a string or an array of keys.
    • In a string, keys are separated by . and an index can be written in brackets, so "a[0].b" and "a.0.b" mean the same thing.
    • In an array, each item is one key, so ["a.b"] writes to a key literally named "a.b".
  • When an intermediate value is missing, create a container for it: an array if the next key is an index, otherwise a plain object. An index is a whole non-negative number without leading zeros, like 0 or 12 ("01" and "-1" are object keys).
  • When an intermediate value exists but is a primitive or null, replace it with a new container chosen by the same rule.
  • Existing objects and arrays on the path are reused, so sibling keys stay untouched.
  • Any value can be stored, including undefined and null.
  • An empty path ("" or []) changes nothing.
  • For safety, if any key in the path is __proto__, constructor or prototype, change nothing. Your set must never be a way to pollute Object.prototype.

This is the counterpart to the Get challenge.