I was wondering if there is a neat way of doing this:
if (app && app.object && app.object.foo) {
alert(app.object.foo.bar);
}
This is really long and "ugly".
I found out that Angular2 has something really great for cases like this. But I think it's only for templates:
<div>{{this?.object?.foo?.bar}}</div>
This got me really exited because I have a lot of code that looks just like the first example. It get's the job done but I'm really hopping there is something more sophisticated.
I was wondering if there is a neat way of doing this:
if (app && app.object && app.object.foo) {
alert(app.object.foo.bar);
}
This is really long and "ugly".
I found out that Angular2 has something really great for cases like this. But I think it's only for templates:
<div>{{this?.object?.foo?.bar}}</div>
This got me really exited because I have a lot of code that looks just like the first example. It get's the job done but I'm really hopping there is something more sophisticated.
Share Improve this question asked Jun 29, 2016 at 11:19 drinovcdrinovc 5515 silver badges16 bronze badges 2 |4 Answers
Reset to default 10Many languages have this functionality, some call it safe navigation operator
or even Elvis operator
(yes haha).
JavaScript does not have this functionality. If you are open to use CoffeeScript
, you might take a look at the existential operator.
But, if you want to keep with JS, you should take a look at lodash.get()
, which lets you do something like this:
var object = { 'a': [{ 'b': { 'c': 3 } }] };
_.get(object, 'a[0].b.c');
// → 3
_.get(object, 'a.b.d.c', 'default');
// → 'default'
I found this solution sometime back on net :
function checkNested(obj /*, level1, level2, ... levelN*/) {
var args = Array.prototype.slice.call(arguments, 1);
for (var i = 0; i < args.length; i++) {
if (!obj || !obj.hasOwnProperty(args[i])) {
return false;
}
obj = obj[args[i]];
}
return true;
}
var test = {level1:{level2:{level3:'level3'}} };
checkNested(test, 'level1', 'level2', 'level3'); // true
checkNested(test, 'level1', 'level2', 'foo'); // false
jsFiddle : https://jsfiddle.net/nikdtu/kjsxnunp/
not out of the box.
var string = function(v){ return v == null? "": String(v) } //because I usually want this behaviour
var fetch = function(path, obj){
path instanceof Array || (path = string(path).split('.'));
for(var i=0, v=obj; i<path.length; v = v[path[i++]])
if(v == null) return void 0;
return v;
}
and your code:
if(fetch("object.foo", app)){
alert(app.object.foo.bar);
}
or even more direct:
alert( fetch("object.foo.bar", app) );
//will alert undefined, if the path could not be resolved
Argument order is this way around, because this function is usually curried in my code, so I can do sth like:
arr.map(fetch('some.deeper.path'));
Slightly shorter (than original) way in JavaScript (less readable)
if ((a = app) && (o = a.object) && (f = o.foo)) {
alert(f.bar);
}
The way I'd write it in CoffeeScript (my preferred method)
alert f.bar if f = app?.object?.foo?
If you're really into using Javascript and want it to appear clean, you could use something like:
function ifExist (name) { // Returns undefined if it doesn't exist
arr = name.split('.');
obj = eval('if (typeof '+(x = arr.splice(0,1)[0])+' !== \'undefined\') '+x);
for (i = 0; i < arr.length; i++) {
if (typeof obj === 'undefined') {
return obj;
}
else {
obj = obj[arr[i]];
}
}
return obj
}
alert(ifExist('app.object.foo.bar'));
Because it uses eval, it could pose a security risk if app.object.foo.bar
is generated by a the user. It works in simple cases (such as the one you posted above), but using something like app.object['foo'].bar
in it would probably break it without adding some input checks.
app
orapp.object
orapp.object.foo
is not truthy like number0
this will return false. – eko Commented Jun 29, 2016 at 11:34