26

I have to check deeply-nested object property such as YAHOO.Foo.Bar.xyz.

The code I'm currently using is

if (YAHOO && YAHOO.Foo && YAHOO.Foo.Bar && YAHOO.Foo.Bar.xyz) {
    // operate on YAHOO.Foo.Bar.xyz
}

This works, but looks clumsy.

Is there any better way to check such deeply nested property?

Morgan Cheng
  • 73,950
  • 66
  • 171
  • 230
  • 3
    Can you not just do `if (YAHOO.Foo.Bar.xyz)`? – Michael Berkowski Aug 03 '11 at 13:33
  • 1
    http://enterprise-js.com/34 <-- is a joke btw – Joseph Marikle Aug 03 '11 at 13:41
  • 6
    @Michael, you can't just do `if (YAHOO.Foo.Bar.xyz)` because if Foo or Bar doesn't exist, then `if (YAHOO.Foo.Bar.xyz)` will throw an exception because of the dereference of the intermediate values that are undefined. – jfriend00 Aug 03 '11 at 13:46
  • 1
    This is probably the best way to check; using a `try...catch` will impact performance - when an exception is thrown, the code will slow down. [jsPerf test](http://jsperf.com/checking-for-deep-nested-properties). – Digital Plane Aug 03 '11 at 14:05
  • 1
    @Digital Plane - But, if the normal case is that it exists, then the try/catch is 19x faster than the multiple if tests (in Chrome). – jfriend00 Aug 03 '11 at 15:37
  • @jfriend00 - So, you can probably choose the faster based on whether you think the property will exist when your code runs. – Digital Plane Aug 04 '11 at 10:44

7 Answers7

20

If you expect YAHOO.Foo.Bar to be a valid object, but want to make your code bulletproof just in case it isn't, then it can be cleanest to just put a try catch around it and let one error handler catch any missing segment. Then, you can just use one if condition instead of four that will detect if the terminal property exists and a catch handler to catch things if the intermediate objects don't exist:

try {
    if (YAHOO.Foo.Bar.xyz) {
        // operate on YAHOO.Foo.Bar.xyz
} catch(e) {
    // handle error here
}

or, depending upon how your code works, it might even just be this:

try {
    // operate on YAHOO.Foo.Bar.xyz
} catch(e) {
    // do whatever you want to do when YAHOO.Foo.Bar.xyz doesn't exist
}

I particularly use these when dealing with foreign input that is supposed to be of a particular format, but invalid input is a possibility that I want to catch and handle myself rather than just letting an exception propagate upwards.

In general, some javascript developers under-use try/catch. I find that I can sometimes replace 5-10 if statements checking input with a single try/catch around a larger function block and make the code a lot simpler and more readable at the same time. Obviously, when this is appropriate depends upon the particular code, but it's definitely worth considering.

FYI, if the usual operation is to not throw an exception with the try/catch, it can be a lot faster than a bunch of if statements too.


If you don't want to use the exception handler, you can create a function to test any arbitrary path for you:

function checkPath(base, path) {
    var current = base;
    var components = path.split(".");
    for (var i = 0; i < components.length; i++) {
        if ((typeof current !== "object") || (!current.hasOwnProperty(components[i]))) {
            return false;
        }
        current = current[components[i]];
    }
    return true;
}

Example usage:

var a = {b: {c: {d: 5}}};
if (checkPath(a, "b.c.d")) {
    // a.b.c.d exists and can be safely accessed
}
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • 12
    Don't abuse try catch like that. Dear god my eyes. And even an empty catch block. This is a code smell if I've ever seen one. – Raynos Nov 21 '11 at 20:44
  • 7
    @Raynos - Why is this abusing try/catch? It makes more sense to me than multiple nested if statements to check all the intermediate levels. An empty catch just means you purposely want to do nothing if the intermediate levels don't exist (a reasonable design choice). This is perfectly fine code. Did you actually downvote because you prefer a different approach? There is absolutely nothing wrong with using try/catch like this. – jfriend00 Nov 21 '11 at 22:08
  • 2
    "the occurrence of exceptions, special conditions that change the normal flow of program execution." I find it a massive stretch to claim that the property not existing is a "special condition that changes the normal flow of program execution". I think your using the wrong tool for the job. – Raynos Nov 21 '11 at 22:23
  • 2
    An intermediate property not existing when you normally expect it to exist can very well be "a special condition that changes the normal flow of program execution" and seems a perfect use for exceptions. You seem to have an opinion that exceptions should only be used for extraordinary things, but they are just another error return/propagation mechanism that can be used much more efficiently than multiple `if` statements in many circumstances, particularly when you are dealing with foreign input that you don't necessarily control and it could be very expensive to test every single operation. – jfriend00 Nov 21 '11 at 22:28
  • 1
    I use exceptions all them all the time when operating on foreign HTML (HTML not under my control) that is supposed to adhere to a particular format, but might have errors in it. It's particularly useful when there's a bunch of housekeeping in the beginning (parsing structure, obtaining data, etc...) and then the final result is applied at the end. I surround the whole block with a try/catch and if any of the structure isn't as expected, it throws an error and I catch all errors in one place very efficiently rather than using dozens of `if` statements. Makes the code much more readable too. – jfriend00 Nov 21 '11 at 22:31
  • it's fine to use try catch on foreign input. You have no control over it. The author has control over his `YUI` object. I think he just needs to refactor his code – Raynos Nov 21 '11 at 22:50
  • @jfriend00 No downvote, but I'm also really against using a `try catch` to handle something that can easily be hanlded with if statements. The empty catch looks so inelegant. Also, if you're going to use the `try catch`, there's no need for `if (YAHOO.Foo.Bar.xyz)`, just use `YAHOO.Foo.Bar.xyz.doMe()`. Try catch code can be hard to follow as you add more code in the try/catch section. At the very least, you need to properly document why you're using a try catch – Ruan Mendes Feb 15 '13 at 18:09
  • @JuanMendes - the OP already had the four `if` conditions and asked for other options. This is another option. I personally think the four `if` tests seems kind of clumsy. Exception handlers can be very useful when you are not expecting these args to ever have a problem, but still want to handle the case where they might be. You can often bracket a large block of code with an exception handler and avoid a lot of `if` checks. If you get used to using exceptions in appropriate places, you will find they can be very elegant, save a lot of code and be simpler to code and follow. – jfriend00 Feb 15 '13 at 18:22
  • @jfriend00 I find that large chunks of code inside a `try catch` are exception traps (they swallow unwanted exceptions), especially in JS where you can't specify catch a type of exception. I much prefer something like what Matt suggested. It's a lot more explicit about what you're actually testing for than a `try/catch`. Less to type, easier to read. Not as fast but it's highly unlikely that this kind of test would be the bottleneck in a program, I abhor micro optimization that sacrifice readability. I think a `defined ` function better satisfies the requirement for the simplest approach. – Ruan Mendes Feb 15 '13 at 18:40
  • @JuanMendes - either Matt's idea or my idea will work. Exception handlers have their place in javascript programming and many developers don't know much about them or feel like they seem foreign to what they know. I have blocks of code where a single exception handler replaces many nested `if` checks and makes much cleaner and more readable code. – jfriend00 Feb 15 '13 at 18:46
  • @JuanMendes - I generally wouldn't use an exception handler to handle an expected, regular condition, but I think they can work very well for successfully handling unexpected conditions without invading/complicating your code with lots of `if` checks. You're welcome to have a different opinion and vote for Matt's answer. There's nothing technically wrong with this answer - it's a different way of doing it that I believe is appropriate in some contexts. – jfriend00 Feb 15 '13 at 18:47
6

Consider this utility function:

function defined(ref, strNames) {
    var name;
    var arrNames = strNames.split('.');

    while (name = arrNames.shift()) {        
        if (!ref.hasOwnProperty(name)) return false;
        ref = ref[name];
    } 

    return true;
}

Usage:

if (defined(YAHOO, 'Foo.Bar.xyz')) {
    // operate on YAHOO.Foo.Bar.xyz
}

Live demo: http://jsfiddle.net/DWefK/5/

Šime Vidas
  • 182,163
  • 62
  • 281
  • 385
  • I think you need an explicit check for undefined in the while loop test, rather than just a truthiness test. At the moment defined({a:''}, 'a.b') === true. Nice approach though, I like it! – codebox Mar 24 '14 at 13:58
  • @codebox I wasn't satisfied with my code so I did a rewrite. What do you think? – Šime Vidas Mar 24 '14 at 14:53
  • I would love to have something like this in underscore or lodash – Jonathan Mar 11 '16 at 13:19
  • Oh lodash actually has it. _.get(), awesome! Even has a default parameter. – Jonathan Mar 11 '16 at 13:26
  • It's a very nice solution. But if the reference object is undefined the code breaks. I think a check if the reference object is defined or not will solve the problem. [Your solution](https://jsbin.com/honeki/edit?js,console), [Updated one](https://jsbin.com/powiqux/1/edit?js,console) – Shahid Mar 14 '19 at 02:20
6
var _ = {};

var x = ((YAHOO.Foo || _).Bar || _).xyz;
Kernel James
  • 3,752
  • 25
  • 32
3

If you need to check the correctness of the path, rather than the existance of the "xyz" member on the "YAHOO.Foo.Bar" object, it will probably be easiest to wrap the call in a try catch:

var xyz;
try {
    xyz = YAHOO.Foo.Bar.xyz;
} catch (e) {
    // fail;
};

Alternately, you can do some string-kong-fu-magicTM:

function checkExists (key, obj) {
    obj = obj || window;
    key = key.split(".");

    if (typeof obj !== "object") {
        return false;
    }

    while (key.length && (obj = obj[key.shift()]) && typeof obj == "object" && obj !== null) ;

    return (!key.length && typeof obj !== "undefined");
}

The use as follows:

if (checkExists("YAHOO.Foo.Bar.xyz")) {
    // Woo!
};
Matt
  • 74,352
  • 26
  • 153
  • 180
  • When would you EVER use everything involved in the checkExists function rather than the try/catch method? – jfriend00 Aug 03 '11 at 13:50
  • I just don't see the point of the `typeof obj !== "object"` tests. Why be restrictive? – hugomg Aug 03 '11 at 13:52
  • @jfriend00: The string method allows you to find the point where things break down (if you happen to want that as well). Passing object descriptions via string also makes sense in some cases, like custom module systems. – hugomg Aug 03 '11 at 13:56
  • @missingno: It checks the provided "obj" by the user (which is optional; it defaults to `window` if it isn't provided), is actually an object (it stops the function breaking if the user passes the wrong argument, basically!) – Matt Aug 03 '11 at 14:06
  • 1
    @jfriend00: The `try/catch` is a bit to type if you've got a lot of places where you want to check the existence of an object path. – Matt Aug 03 '11 at 14:08
  • @Matt - OK, but the try/catch is a ton faster than splitting arrays and looping and works easily when some of the intermediate references are variables too. I find that try/catch is way underused in JS. I can often replace a whole bunch of if statements in a function with one surrounding try/catch and not only protect my code from the things I was manually checking, but other errors too. Obviously, when it works best depends upon what your code is doing, but most JS programmers should us it more than they do. – jfriend00 Aug 03 '11 at 14:21
  • @jfriend: In relative terms I agree. However in the real world computers today, the +-ms difference does not equate to much. – Matt Aug 03 '11 at 14:30
1

This problem is solved quite beautifully by coffeescript (which compiles down to javascript):

if YAHOO.Foo?.Bar?.xyz
  // operate on YAHOO.Foo.Bar.xyz
Albert Wu
  • 11
  • 1
0

use a try catch.

a={
  b:{}
};

//a.b.c.d?true:false; Errors and stops the program.

try{
  a.b.c.d;
}
catch(e){
  console.log(e);//Log the error
  console.log(a.b);//This will run
}
qw3n
  • 6,236
  • 6
  • 33
  • 62
0

I actually voted to close the question as duplicate of javascript convert dotnotation string into objects.

However, I guess it's a different topic, but the answer there might still be helpful if you don't want to try-catch all the time.

Community
  • 1
  • 1
jAndy
  • 231,737
  • 57
  • 305
  • 359