131

a "problem" which i have every now and then is that i have an object e.g. user = {} and through the course of using the app this gets populated. Let's say somwhere, after an AJAX call or something i do this:

user.loc = { lat: 50, long: 9 } 

At another place i want to check if user.loc.lat exists.

if (user.loc.lat) { // do something } 

If it does not exists, this will cause an error. If user.loc.lat is undefined, user.loc of course is undefined as well.

"Cannot read property 'lat' of null" - Dev Tools error 

That means I need to check it like this:

if (user.loc) { if (user.loc.lat) { // do something } } 

or

if (user.loc && user.loc.lat) { // do something } 

This isn't really pretty and the bigger my objects are the worse it gets - obviously (imagine 10 levels of nesting). It kind bums me that if(user.loc.lat) isn't just returning false if user.loc is undefined as well.

What's the ideal way to check situations like this?

3
  • 11
    Try this if(user && user.loc && user.loc.lat) { Commented May 22, 2014 at 13:58
  • You can check value of null and undefined using typeof Commented May 22, 2014 at 14:00
  • 1
    @AamirAfridi—even though that's how the OP was written, the test should stop at the last object, not the property. e.g. the test will return false if user.loc.lat exists but has a falsey value (such as 0). :-) Commented May 22, 2014 at 14:01

4 Answers 4

172

You can use an utility function like this:

get = function(obj, key) { return key.split(".").reduce(function(o, x) { return (typeof o == "undefined" || o === null) ? o : o[x]; }, obj); } 

Usage:

 get(user, 'loc.lat') // 50 get(user, 'loc.foo.bar') // undefined 

Or, to check only if a property exists, without getting its value:

has = function(obj, key) { return key.split(".").every(function(x) { if(typeof obj != "object" || obj === null || ! x in obj) return false; obj = obj[x]; return true; }); } if(has(user, 'loc.lat')) ... 
Sign up to request clarification or add additional context in comments.

13 Comments

The problem with the get approach is that it may return a property from anywhere in the chain, it doesn't specifically test for the last property.
@RobG How so? As soon as it hits an undefined that just gets passed all the way through and the result is undefined.
@RobG: Updated has for more accurate type checking, so that has('foo.bar.baz') won't fail if foo.bar is, say, a number.
I wish lodash would and a function for this._.has is the closest but doesn't check the value.
Actually, scratch that, it does! Just use _.get stackoverflow.com/a/24046380/654708
|
33

You can combine the checks using lazy and:

if(user.loc && user.loc.lat) { ... 

Or, you use CoffeeScript. And ES2020 has new syntax ( Nullish coalescing Operator ).

user.loc?.lat?. '...' 

which would run the checks for loc property and safeguard against empty objects.

4 Comments

sorry, you were all very fast in answering. Your first approach is also a case that i want to avoid. Consider that i have huge object with 5 to 15 levels of nesting.
You don't have a lot of options here. (1) property checking, manually, or with CoffeeScript, (2) try/catch. If you have an object with 15 levels of nesting and its property may be absent at any level, then I'd think you have some sub-optimal design patterns with you application.
It's an example. I want a way to cover that scenario. I obviously would avoid 15 levels of nesting but what if an old server-side application just does it that way and i have to deal with it? That's why i wanted to cover that scenario
.? is called the optional chaining operator. The nullish coalescing operator` (??) is very different and more similar to the OR operator (||). developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/….
28

Well, javascript has try-catch. Depending on what you actually need to do (i.e. what your else statement would look like if it's undefined), that may be what you want.

example:

try { user.loc.lat.doSomething(); } catch(error) { //report } 

3 Comments

I don't think it's a good idea to throw exceptions instead of if-else, if you know that a var may be unset, I don't consider that an exception, thus you should handle it without throwing in a try-catch. (Of course you might throw in the else-clause) then.
I like this solution for simple stuff that sometimes get harder to read with conditionals. @JonasStensved is pointing no using try catch on this because launching exception (you are not launching it until you write throw, i think) but the problem could be something else goes wrong in the block and you will not be able to detect it on the future.
@pikilon Rule of thumb: Do not use exceptions for flow control. In your case something really is unexpected and an exception, then throw. Otherwise handle it as any other code condition.
9

Try this if(user && user.loc && user.loc.lat) {...}

You can check value of null and undefined using typeof

If .loc has value false than you can try

if(user && user.loc && typeof(user.loc)!=="undefined"){...}

If you have a huge nested object than have a look at

Source.

function checkNested(obj /*, level1, level2, ... levelN*/) { var args = Array.prototype.slice.call(arguments), obj = args.shift(); for (var i = 0; i < args.length; i++) { if (!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 

Update: Try lodash.get

2 Comments

sorry, you were all very fast in answering. This is also a case that i want to avoid. Consider that i have huge object with 5 to 15 levels of nesting.
I hate people down voting without adding a comment.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.