0

Say I have an object named foo, and I would like to perform the following operation:

foo.bar.baz.qwer.asdf = 5;

The issue is that bar may not exist, but if it does exist, then baz inside of it may not exist, etc.

I thought I would be able to do something like this:

foo?.bar?.baz?.qwer?.asdf = 5, but alas, that sort of chaining only seems to work with retrieving values, not setting values.

The desired behavior is that if something like baz already exists, then that object is just accessed (and its existing members are maintained), but if baz does not exist, then that property is created on the fly.

That is, let's say only foo.bar existed. Then after the above assignment operation, the resulting object would be:

foo {
  bar: {
    baz: {
      qwer: {
        asdf: 5
      }
    }
  }
}

So my question is: can this be done with existing language syntax constructs, or must a helper function be created? If so, does this sort of function have a name that I could search for and reference?

  • I think you have too check if each property exists unless you want to overwrite them. – about14sheep Jan 07 '22 at 16:36
  • @about14sheep Yeah, that would work, but it would be too cumbersome, verbose, and error-prone in this case, as I have very deep objects, and need to perform this sort of operation in many different places. – John Smith Jan 07 '22 at 16:37
  • so if `bar` exists, do you not want to keep iterating until you get too `asdf`? Or are you only concerned with the final property in the chain existing? – about14sheep Jan 07 '22 at 16:40
  • This is how PHP array assignment works, but you'd have to roll your own function to make it work in Javascript. – TKoL Jan 07 '22 at 16:44
  • @about14sheep The latter. The final property must be made, and any intermediate objects along the way should be constructed if necessary. – John Smith Jan 07 '22 at 16:46
  • @TKoL Ah, very well. I'll begin work on writing such a function now, and if no one else replies with an answer, I'll answer this question with what I come up with (if I figure it out). – John Smith Jan 07 '22 at 16:46

1 Answers1

0

I have written this:

function setProperty(obj, property, value) {
    const properties = property.split('.');
    const lastProperty = properties.pop();
    for (let prop of properties) {
        if (!obj[prop]) {
            obj[prop] = {};
        }
        obj = obj[prop];
    }
    obj[lastProperty] = value;
}

const startObj = {'a': {}};

setProperty(startObj, 'a.b.c.d.e', 'e is the best letter');
console.log(startObj);

However, it will have unexpected behaviors if one of the intermediate properties exists but isn't an object. Eg test the above function with the starting object {a:{b:2}}

Also, please note that what obj means changes as the function processes. At first, it means the base object, but it goes on to mean each nested object during the loop.

TKoL
  • 13,158
  • 3
  • 39
  • 73
  • 1
    Thanks, this function looks good, and that unexpected behavior _should_ be fine, I think, as that would just be a bug with the application anyway, which should cause a crash and investigation. Thanks. – John Smith Jan 07 '22 at 16:52
  • I found a guy that gave functionally equivalent code to a similar question before: https://stackoverflow.com/questions/18936915/dynamically-set-property-of-nested-object – TKoL Jan 07 '22 at 16:56
  • Ah, there it is. I was looking for that thread but my Googlefu was failing me. Good find – John Smith Jan 07 '22 at 16:56
  • I think my code is better though ;) – TKoL Jan 07 '22 at 16:57
  • 1
    Yeah, I agree. Also, lodash seems to have a `set` function, which I may use, since this project has already imported lodash anyway. Anyways, thanks again! – John Smith Jan 07 '22 at 16:58