$ snippet get-set-by-path

Get and set nested values by path

get(obj, 'a.b[0].c') and set(obj, 'a.b.c', 1) that never throw on missing keys and create intermediate objects on write.

JavaScript
/** "a.b[0].c" or ["a","b",0,"c"] → normalized key array */
function toPath(path) {
  if (Array.isArray(path)) return path;
  return path.replace(/\[(\w+)\]/g, ".$1").split(".").filter(Boolean);
}

/** get(obj, "a.b.0.c", fallback) — undefined-safe */
function get(obj, path, fallback) {
  let cur = obj;
  for (const key of toPath(path)) {
    if (cur == null) return fallback;
    cur = cur[key];
  }
  return cur === undefined ? fallback : cur;
}

/** set(obj, "a.b.0.c", value) — mutates and returns obj; creates {} or [] as needed */
function set(obj, path, value) {
  const keys = toPath(path);
  let cur = obj;
  keys.forEach((key, i) => {
    if (i === keys.length - 1) { cur[key] = value; return; }
    if (cur[key] == null || typeof cur[key] !== "object")
      cur[key] = /^\d+$/.test(keys[i + 1]) ? [] : {};
    cur = cur[key];
  });
  return obj;
}

/** has(obj, "a.b") — true only if the full path exists */
function has(obj, path) {
  let cur = obj;
  for (const key of toPath(path)) {
    if (cur == null || !(key in Object(cur))) return false;
    cur = cur[key];
  }
  return true;
}

How it works

Optional chaining (obj?.a?.b) covers the static case. These helpers are for when the path is data — a column name from config, a field in a form builder, a key the user typed. get walks the path and bails out at the first null, set builds the missing containers, choosing an array when the next key looks like an index.

Gotchas

Try it
Result
Ad slot