How to automatically add properties to an object that is undefined?

Javascript

Javascript Problem Overview


Is there an easy way to automatically add properties to objects if they don't already exist?

Consider the following example:

var test = {}
test.hello.world = "Hello doesn't exist!"

This doesn't work because hello isn't defined.

The reason why I'm asking this is because I have some existing objects for which I don't know if they allready have hello or not. I actually have a lot of these objects in different parts of my code. It is very annoying to always check if hello exists and if it doesn't create a new object like:

var test = {}
if(test.hello === undefined) test.hello = {}
test.hello.world = "Hello World!"

Is there a way to automatically create an object like hello in this example?

I mean something like that in php:

$test = array();  
$test['hello']['world'] = "Hello world";   
var_dump($test);

Output:

array(1) {
  ["hello"] => array(1) {
    ["world"] => string(11) "Hello world"
  }
}

Ok it's an array but in js arrays it is the same problem as with objects.

Javascript Solutions


Solution 1 - Javascript

var test = {};
test.hello = test.hello || {};
test.hello.world = "Hello world!";

If test.hello is undefined, it gets set to an empty object.

If test.hello was previously defined, it stays unchanged.

var test = {
  hello : {
    foobar : "Hello foobar"
  }
};

test.hello = test.hello || {};
test.hello.world = "Hello World";

console.log(test.hello.foobar); // this is still defined;
console.log(test.hello.world); // as is this.

Solution 2 - Javascript

You can use the Logical nullish assignment (??=):

var test = {};
(test.hello ??= {}).world ??= "Hello doesn't exist!";

Solution 3 - Javascript

New object

myObj = {};

recursive function

function addProps(obj, arr, val) {

    if (typeof arr == 'string')
        arr = arr.split(".");

    obj[arr[0]] = obj[arr[0]] || {};

    var tmpObj = obj[arr[0]];

    if (arr.length > 1) {
        arr.shift();
        addProps(tmpObj, arr, val);
    }
    else
        obj[arr[0]] = val;

    return obj;

}

Call it with a dot notated string

addProps(myObj, 'sub1.sub2.propA', 1);

or with an array

addProps(myObj, ['sub1', 'sub2', 'propA'], 1);

and your object will look like this

myObj = {
  "sub1": {
    "sub2": {
      "propA": 1
    }
  }
};

It works with non-empty objects too!

Solution 4 - Javascript

You won't be able to do this without some sort of function, as JavaScript doesn't have a generic getter/setter method for objects (Python, for example, has __getattr__). Here's one way to do it:

function add_property(object, key, value) {
    var keys = key.split('.');

    while (keys.length > 1) {
        var k = keys.shift();

        if (!object.hasOwnProperty(k)) {
            object[k] = {};
        }

        object = object[k];
    }

    object[keys[0]] = value;
}

If you really want to, you could add it to the prototype of Object. You can call it like so:

> var o = {}
> add_property(o, 'foo.bar.baz', 12)
> o.foo.bar.baz
12

Solution 5 - Javascript

Well you could extend the prototype of Object with a function that return a property, but adds it first, if it doesn't exist:

Object.prototype.getOrCreate = function (prop) {
    if (this[prop] === undefined) {
        this[prop] = {};
    }
    return this[prop];
};

var obj = {};

obj.getOrCreate("foo").getOrCreate("bar").val = 1;

Solution 6 - Javascript

Here's a cool version with proxies:

const myUpsert = (input) => {
    const handler = {
        get: (obj, prop) => {
            obj[prop] = obj[prop] || {};
            return myUpsert(obj[prop]);
        }
    };
    return new Proxy(input, handler);
};

And you use it like this:

myUpsert(test).hello.world = '42';

This will add all the missing properties as empty objects, and leave the existing ones untouched. It's really just a proxied version of the classic test.hello = test.hello || {}, albeit much slower (See benchmark here.) But it's also much nicer to look at, especially if you'll be doing it more than one level deep. I wouldn't pick it for performance-heavy data crunching, but it's probably fast enough for a front-end state update (as in Redux).

Note that there's some implicit assumptions here:

  1. The intervening properties are either objects or non-existent. This will choke if test.hello is a string, for example.
  2. That you always want to be doing this for as long as you're using the Proxy instead of the original object.

These are pretty easily mitigated if you only use it in well-bounded contexts (like a reducer body) where there's little chance of accidentally returning the Proxy, and not much else you would want to do with the object.

Solution 7 - Javascript

var test = {}
if(!test.hasOwnProperty('hello')) {
    test.hello = {};
}
test.hello.world = "Hello World!"

Solution 8 - Javascript

This will add a property hello whose value is {world: 'Hello world!'} to the test object, if it doesn't exist. If you have a lot of these objects, you can just iterate over them and apply this function. Note: uses lodash.js

var test = {};
_.defaults(test, { hello: {world: 'Hello world!'} });    

Which is actually a convenience method for saying:

var defaults = _.partialRight(_.assign, function(a, b) {
  return typeof a == 'undefined' ? b : a;
});        
defaults(test, { hello: {world: 'Hello world!'} });

Note: _.defaults uses loops to achieve the same thing as the second block.

P.S. Checkout https://stackoverflow.com/a/17197858/1218080

Solution 9 - Javascript

let test = {};
test = {...test, hello: {...test.hello, world: 'Hello does exist!'}};
console.log(test);

When using the spread operator, the value can be undefined, it'll automatically create an object.

Solution 10 - Javascript

I've come up with something, really custom as well, but it works as far as I have tested.

function dotted_put_var(str,val) {
    var oper=str.split('.');
    var p=window;
    for (var i=0;i<oper.length-1;i++) {
        var x=oper[i];
        p[x]=p[x]||{};
        p=p[x];
    }
    p[oper.pop()]=val;
}

Then, a complex variable can be set like this, ensuring that every links will be created if not already:

dotter_put_var('test.hello.world', 'testvalue'); // test.hello.world="testvalue";

See this working FIDDLE.

Solution 11 - Javascript

I use this:

Object.prototype.initProperty = function(name, defaultValue) {
  if (!(name in this)) this[name] = defaultValue;
};

You can later do f.e.:

var x = {a: 1};
x.initProperty("a", 2); // will not change property a
x.initProperty("b", 3); // will define property b
console.log(x); // => {a: 1, b: 3}

Solution 12 - Javascript

var test = {}
test.hello.world = "Hello doesn't exist!"

This will throw an error obviously as you didn't defined the test.hello

Firstly you need to need define the hello key then inside you can assign any key. But if you want to create key if not exists then you can do following thing

test.hello = test.hello || {};

The above statement will create the test.hello object if not defined and if it is defined then it will assign the same value as it is previously

Now you can assign any new key inside the test.hello

test.hello.world = "Everything works perfect";

test.hello.world2 = 'With another key too, it works perfect';

Solution 13 - Javascript

I think the easiest way is to use _.set from Lodash

 _.set({}, 'a[0].b.c', 4);
// => { a: [{ b: { c: 4 } }] }

Solution 14 - Javascript

I've made some changes on columbus's answer to allow create arrays:

function addProps(obj, arr, val) {

  if (typeof arr == 'string')
    arr = arr.split(".");

  var tmpObj, isArray = /^(.*)\[(\d+)\]$/.exec(arr[0])
  if (isArray && !Number.isNaN(isArray[2])) {
    obj[isArray[1]] = obj[isArray[1]] || [];
    obj[isArray[1]][isArray[2]] = obj[isArray[1]][isArray[2]] || {}
    tmpObj = obj[isArray[1]][isArray[2]];
  } else {
    obj[arr[0]] = obj[arr[0]] || {};
    tmpObj = obj[arr[0]];
  }

  if (arr.length > 1) {
    arr.shift();
    addProps(tmpObj, arr, val);
  } else
    obj[arr[0]] = val;

  return obj;

}


var myObj = {}
addProps(myObj, 'sub1[0].sub2.propA', 1)
addProps(myObj, 'sub1[1].sub2.propA', 2)

console.log(myObj)

I think that is possible to allow use "sub1[].sub2..." to just push into the sub1 array, instead of specify the index, but that's enough for me now.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMarcel GwerderView Question on Stackoverflow
Solution 1 - JavascriptxbonezView Answer on Stackoverflow
Solution 2 - JavascriptDexBGView Answer on Stackoverflow
Solution 3 - JavascriptcolumbusView Answer on Stackoverflow
Solution 4 - JavascriptBlenderView Answer on Stackoverflow
Solution 5 - JavascriptbasilikumView Answer on Stackoverflow
Solution 6 - JavascriptJohn NeuhausView Answer on Stackoverflow
Solution 7 - JavascriptShawn31313View Answer on Stackoverflow
Solution 8 - Javascriptholographic-principleView Answer on Stackoverflow
Solution 9 - JavascriptNickView Answer on Stackoverflow
Solution 10 - JavascriptFrederik.LView Answer on Stackoverflow
Solution 11 - JavascriptMichal PolitzerView Answer on Stackoverflow
Solution 12 - JavascriptVIKAS KOHLIView Answer on Stackoverflow
Solution 13 - JavascriptMamadou OuologuemView Answer on Stackoverflow
Solution 14 - JavascriptAlisson NunesView Answer on Stackoverflow