I have json based data structure with objects containing nested objects. In order to access a particular data element I have been chaining references to object properties together. For example:
var a = b.c.d;
If b or b.c is undefined, this will fail with an error. However, I want to get a value if it exists otherwise just undefined. What is the best way to do this without having to check that every value in the chain exists?
I would like to keep this method as general as possible so I don't have to add huge numbers of helper methods like:
var a = b.getD();
or
var a = helpers.getDFromB(b);
I also want to try to avoid try/catch constructs as this isn't an error so using try/catch seems misplaced. Is that reasonable?
Any ideas?
Standard approach:
var a = b && b.c && b.c.d && b.c.d.e;
is quite fast but not too elegant (especially with longer property names).
Using functions to traverse JavaScipt object properties is neither efficient nor elegant.
Try this instead:
try { var a = b.c.d.e; } catch(e){}
in case you are certain that a
was not previously used or
try { var a = b.c.d.e; } catch(e){ a = undefined; }
in case you may have assigned it before.
This is probably even faster that the first option.
If you would like to have a dynamic access with irregular number of properties at hand, in ES6 you might easily do as follows;
function getNestedValue(o,...a){
var val = o;
for (var prop of a) val = typeof val === "object" &&
val !== null &&
val[prop] !== void 0 ? val[prop]
: undefined;
return val;
}
var obj = {a:{foo:{bar:null}}};
console.log(getNestedValue(obj,"a","foo","bar"));
console.log(getNestedValue(obj,"a","hop","baz"));
probably it's may be simple:
let a = { a1: 11, b1: 12, c1: { d1: 13, e1: { g1: 14 }}}
console.log((a || {}).a2); => undefined
console.log(((a || {}).c1 || {}).d1) => 13
and so on.
Gets the value at path
of object
. If the resolved value is undefined
, the defaultValue
is returned in its place.
In ES6 we can get nested property from an
Object
like below code snippet.
const myObject = {
a: {
b: {
c: {
d: 'test'
}
}
},
c: {
d: 'Test 2'
}
},
isObject = obj => obj && typeof obj === 'object',
hasKey = (obj, key) => key in obj;
function nestedObj(obj, property, callback) {
return property.split('.').reduce((item, key) => {
if (isObject(item) && hasKey(item, key)) {
return item[key];
}
return typeof callback != undefined ? callback : undefined;
}, obj);
}
console.log(nestedObj(myObject, 'a.b.c.d')); //return test
console.log(nestedObj(myObject, 'a.b.c.d.e')); //return undefined
console.log(nestedObj(myObject, 'c.d')); //return Test 2
console.log(nestedObj(myObject, 'd.d', false)); //return false
console.log(nestedObj(myObject, 'a.b')); //return {"c": {"d": "test"}}
An old question, and now days we have Typescript projects so often that this question seems irrelevant, but I got here searching for the same thing, so I made a simple function to do it. Your thoughts about not using try/catch is too strict for my taste, after all the seek for undefined.x
will cause an error anyway.
So with all that, this is my method.
function getSafe (obj, valuePath) {
try { return eval("obj." + valuePath); }
catch (err) { return null; }
}
To use this we have to pass the object. I tried to avoid that, but there was not other way to get scope into it from another function (there is a whole bunch of questions about this in here). And a small test set to see what we get:
let outsideObject = {
html: {
pageOne: {
pageTitle: 'Lorem Ipsum!'
}
}
};
function testme() {
let insideObject = { a: { b: 22 } };
return {
b: getSafe(insideObject, "a.b"), // gives: 22
e: getSafe(insideObject, "a.b.c.d.e"), // gives: null
pageTitle: getSafe(outsideObject, "html.pageOne.pageTitle"), // gives: Lorem Ipsum!
notThere: getSafe(outsideObject, "html.pageOne.pageTitle.style") // gives: undefined
}
}
testme();
UPDATE:
Regarding the use of eval
I think that eval is a tool to use carefully and not the devil itself. In this method, the user does not interfere with eval since it is the developer that is looking for a property by its name.
If you care about syntax, here's a cleaner version of Hosar's answer:
function safeAccess(path, object) {
if (object) {
return path.reduce(
(accumulator, currentValue) => (accumulator && accumulator[currentValue] ? accumulator[currentValue] : null),
object,
);
} else {
return null;
}
}
I have similar code like below and I want If I clicked in Child Div then Parent Div click event should not trigger. <div class="parentDiv" (click)="parentDiv()"> <div class="childDiv" (...
I have similar code like below and I want If I clicked in Child Div then Parent Div click event should not trigger. <div class="parentDiv" (click)="parentDiv()"> <div class="childDiv" (...
I have the following code: Html <div ng-controller="MyCtrl"> {{message}}! <button ng-click="changeMessage()">change Message</button> </div> AngularJs var myApp = ...
I have the following code: Html <div ng-controller="MyCtrl"> {{message}}! <button ng-click="changeMessage()">change Message</button> </div> AngularJs var myApp = ...
I want to pass an Id when I open a model from bootstrap. I have a table and want to click on the row to do this. I am using master-pages and I am loading the jquery and bootstrap at the end of the ...
I want to pass an Id when I open a model from bootstrap. I have a table and want to click on the row to do this. I am using master-pages and I am loading the jquery and bootstrap at the end of the ...
Having a weird issue, on Windows 10 in the screen settings on some laptops the default value (the recommended) is 125% so when opening a web page everything is to big because the page was build for ...
Having a weird issue, on Windows 10 in the screen settings on some laptops the default value (the recommended) is 125% so when opening a web page everything is to big because the page was build for ...