How to automatically add properties to an object that is undefined?
JavascriptJavascript 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:
- The intervening properties are either objects or non-existent. This will choke if
test.hello
is a string, for example. - 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.