0

I am working with a javascript object and need to set a value deep in the object structure.

Lets say:

a.b.c.d.e.f.g = "some value";

I don't know if all of these objects are created yet, so I end up doing:

a = a || {};
a.b = a.b || {};
a.b.c = a.b.c || {};
a.b.c.d = a.b.c.d || {};
a.b.c.d.e = a.b.c.d.e || {};
a.b.c.d.e.f = a.b.c.d.e.f || {};
a.b.c.d.e.f.g = "some value";

Surely there is a better way to do this right?

JuggernautDad
  • 1,135
  • 2
  • 13
  • 28
  • 1
    You could write a function that's called like `set(a, ["b", "c", "d", ...], "some value");` –  Jan 31 '19 at 23:14
  • 1
    https://stackoverflow.com/questions/18936915/dynamically-set-property-of-nested-object – epascarello Jan 31 '19 at 23:19
  • What do you mean by _"I don't know if all of these objects are created yet"_? Is the requirement to create the data structure or traverse an existing data structure? – guest271314 Jan 31 '19 at 23:21
  • it's a setting tree where a is the global list of settings. in my specific case, say a.b.c.d.x has already been set, then I would need to create the e and f object, and add the g property without screwing up anything above it. – JuggernautDad Jan 31 '19 at 23:29
  • Is the existing object data structure `{"a":{"b":{"c":{"d":{}}}}}`? – guest271314 Jan 31 '19 at 23:32
  • here is one possible structure { "a": { "b" : { "c": { "d": { "l": { "name": "value" }, "m": { "name": "value" }, "n": { "name": "value" } }, "t": { "name": "value" }, "u": { "name": "value" }, "v": { "name": "value" } } }, "x": { "name": "value" }, "y":{ "name": "value" }, "z":{ "name": "value" } } } – JuggernautDad Jan 31 '19 at 23:42
  • in my setting, i only know the path the setting needs to go to, and the value. I dont know for certain what other settings (objects) have been created (and it can change by the minute). – JuggernautDad Jan 31 '19 at 23:43
  • @epascarello, your link solved my issue! thanks! – JuggernautDad Jan 31 '19 at 23:44
  • @JustinY17 _"i only know the path the setting needs to go to"_ If the requirement is to set property `e` at existing object `d` ("path") and `f` and `g` at `e` it should not matter what the remainder of the object is. You can get the "path" using destructuring or dot notation and set the properties and values at the "path" object directly without traversal. – guest271314 Jan 31 '19 at 23:48

2 Answers2

2

Easiest way is to use a string, split on the dots, and loop. When you loop you check to see if it exists, if it does, you use it. If it does not than you create a new object. You do that until you get to the end where you set the value.

const setValue = (obj, path, value) => {
  path.split('.') // split on the dots
    .reduce((o, k, i, a) => {
      o[k] = (i + 1 === a.length) // check if we are at last index
        ? value  // if last index use the value
        : (o[k] || {})  // else return object or set new one
      return o[k]  // return the current step in the object
    }, obj)  // start location
}

setValue(window, 'a.b.c.d.e.f.g', 'some value')
console.log(a.b.c.d.e.f.g)


var foo = { a : { b: {z : {} } } }
setValue(foo, 'a.b.c.d.e.f.g', 'another value')
console.log(foo)
epascarello
  • 204,599
  • 20
  • 195
  • 236
1

I'd use reduce to iterate over all props but the last, creating an object at the nested property if necessary, and returning the nested value. Then, on the last object, assign to the last property:

a = window.a || {};
const props = ['b', 'c', 'd', 'e', 'f'];
const lastProp = 'g';
const lastObj = props.reduce((a, prop) => {
  if (!a[prop]) a[prop] = {};
  return a[prop];
}, a);
lastObj[lastProp] = 'some value';
console.log(a);
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320