In my code, I deal with an array that has some entries with many objects nested inside one another, whereas some do not. It looks something like the following:
// Where this array is hundreds of entries long, with a mix // of the two examples given var test = [>>, ];
This is giving me problems, because I need to iterate through the array at times, and the inconsistency is throwing me errors like so:
for (i=0; i
I am aware that I can say if(a.b) < console.log(a.b.c)>, but this is extraordinarily tedious in cases where there are up to 5 or 6 objects nested within one another. Is there any other (easier) way that I can have it only do the console.log if it exists, but without throwing an error?
31.5k 22 22 gold badges 109 109 silver badges 132 132 bronze badges asked Feb 8, 2013 at 22:16 3,629 5 5 gold badges 27 27 silver badges 48 48 bronze badgesThe error is probably a regular Javascript exception, so try the try..catch statement. That said, an array that contains wildly heterogenous elements looks like a design issue to me.
Commented Feb 8, 2013 at 22:17If your structure isn't consistent across the items, then what's wrong with checking for existence? Really, I'd use if ("b" in a && "c" in a.b) . It may be "tedious", but that's what you get for inconsistency. normal logic.
Commented Feb 8, 2013 at 22:21 Why would you access non-existing properties, why don't you know how the objects look like? Commented Feb 8, 2013 at 22:22I can understand why somebody would not want an error to crash everything. You can't always rely on an object's properties to exist or not exist. If you have something in place that can handle the event that the object is malformed, then you're code is much more efficient and less fragile.
Commented Apr 9, 2013 at 20:19 You'd be surprised at how many objects/arrays are malformed in real life situations Commented Sep 8, 2017 at 2:42Update:
// use it like this obj?.a?.lot?.of?.properties
Solution for JavaScript before ECMASCript 2020 or TypeScript older than version 3.7:
A quick workaround is using a try/catch helper function with ES6 arrow function:
function getSafe(fn, defaultVal) < try < return fn(); >catch (e) < return defaultVal; >> // use it like this console.log(getSafe(() => obj.a.lot.of.properties)); // or add an optional default value console.log(getSafe(() => obj.a.lot.of.properties, 'nothing'));
answered Feb 20, 2017 at 16:23
44.4k 17 17 gold badges 112 112 silver badges 132 132 bronze badges
I love it! the only thing I would add is a console.warn inside the catch, so that you know of the error but it continues on.
Commented Dec 11, 2018 at 10:31Catching all exceptions without re-throwing is bad, and generally using exceptions as part of the expected flow of execution is also not great -- even though in this case it's pretty well contained.
Commented Aug 25, 2019 at 11:28Oh man, you're my hero now!! try and catch did it for me after all my failed efforts with Object.assign, 'OR' operator, ternary operator, etc nothing works in the presence of an undefined error. Thank you!!
– user7622147 Commented Feb 11, 2022 at 15:11 beautiful operator .? - in python we dont have it and must use lodash package Commented Aug 18, 2023 at 10:14 Except it doesn't seem to work for undefined array elements: data[0]? . Commented Feb 8 at 21:38What you are doing raises an exception (and rightfully so). You can always do:
try< window.a.b.c >catch(e)
But I wouldn't, instead think of your use case.
Why are you accessing data, 6 levels nested that you are unfamiliar of? What use case justifies this?
Usually, you'd like to actually validate what sort of object you're dealing with.
Also, on a side note you should not use statements like if(a.b) because it will return false if a.b is 0 or even if it is "0". Instead check if a.b !== undefined
43.5k 15 15 gold badges 94 94 silver badges 100 100 bronze badges answered Feb 8, 2013 at 22:18 Benjamin Gruenbaum Benjamin Gruenbaum 275k 89 89 gold badges 514 514 silver badges 512 512 bronze badgesIn regards to your first edit: It is justified; I am dealing with JSON structured database entries, such that the objects will scale multiple levels of fields (ie. entries.users.messages.date etc., where not all cases have data entered)
Commented Feb 8, 2013 at 22:23"it will return true if a.b is 0" - nope. typeof a.b === "undefined" && a.b!=null - unnecessary to do the second part after the first, and it makes more sense to just do if ("b" in a)
Commented Feb 8, 2013 at 22:23@Ian yeah, I obviously meant it the other way around, it will return false even if a.b is "0". Nice catch
Commented Feb 8, 2013 at 22:24@BenjaminGruenbaum Sounds good, wasn't sure if you meant that. Also, I think you want typeof a.b ! == "undefined" && a.b!=null` - notice the !==
Commented Feb 8, 2013 at 22:26If you don't want to tedium of a.b && a.b.c && console.log(a.b.c) then this is the only way to consistently log unknowns.
Commented Feb 8, 2013 at 22:31If I am understanding your question correctly, you want the safest way to determine if an object contains a property.
The easiest way is to use the in operator.
window.a = "aString"; //window should have 'a' property //lets test if it exists if ("a" in window) < //true >if ("b" in window) < //false >
Of course you can nest this as deep as you want
if ("a" in window.b.c)
Not sure if this helps.
26.3k 13 13 gold badges 86 86 silver badges 94 94 bronze badges answered Feb 8, 2013 at 23:16 matt weiss matt weiss 355 1 1 silver badge 4 4 bronze badgesYou can't safely nest this as deeply as you want. What if window.b is undefined? You will get a type error: Cannot use 'in' operator to search for 'c' in undefined
Commented Aug 28, 2017 at 23:34Try this. If a.b is undefined , it will leave the if statement without any exception.
if (a.b && a.b.c)43.5k 15 15 gold badges 94 94 silver badges 100 100 bronze badges answered Jun 28, 2017 at 11:15 259 3 3 silver badges 3 3 bronze badges
If you are using lodash, you could use their has function. It is similar to the native "in", but allows paths.
var testObject = >>; if(_.has(testObject, 'a.b.c')) < //Safely access your walrus here >
43.5k 15 15 gold badges 94 94 silver badges 100 100 bronze badges
answered Aug 7, 2015 at 19:05
414 5 5 silver badges 10 10 bronze badges
Best, we can use _.get() with default for easy read : _.get(object, 'a.b.c', 'default');
Commented Jun 18, 2018 at 13:53
If you use Babel, you can already use the optional chaining syntax with @babel/plugin-proposal-optional-chaining Babel plugin. This would allow you to replace this:
console.log(a && a.b && a.b.c);
console.log(a?.b?.c);
31.5k 22 22 gold badges 109 109 silver badges 132 132 bronze badges
answered Feb 24, 2019 at 23:33
8,366 4 4 gold badges 49 49 silver badges 72 72 bronze badges
I use undefsafe religiously. It tests each level down into your object until it either gets the value you asked for, or it returns "undefined". But never errors.
answered May 12, 2016 at 16:32 martinedwards martinedwards 5,817 1 1 gold badge 35 35 silver badges 37 37 bronze badges that is similar to lodash _.get Commented Jul 21, 2016 at 3:32 Good shout! Still useful if you don't need the other features of lodash. Commented Jul 22, 2016 at 14:04This is a common issue when working with deep or complex JSON object, so I try to avoid try/catch or embedding multiple checks which would make the code unreadable. I usually use this little piece of code in all my projects to do the job.
/* Example: getProperty(myObj, 'aze.xyz', 0) // return myObj.aze.xyz safely * accepts array for property names: * getProperty(myObj, ['aze', 'xyz'], ) */ function getProperty(obj, props, defaultValue) < var res, isvoid = function(x) if(!isvoid(obj)) < if(isvoid(props)) props = []; if(typeof props === "string") props = props.trim().split("."); if(props.constructor === Array) < res = props.length>1 ? getProperty(obj[props.shift()], props, defaultValue) : obj[props[0]]; > > return typeof res === "undefined" ? defaultValue: res; >
31.5k 22 22 gold badges 109 109 silver badges 132 132 bronze badges
answered Sep 22, 2016 at 13:53
61 1 1 silver badge 1 1 bronze badge
You can use optional chaining from the ECMAScript standard. Like this:
a?.b?.c?.d?.func?.()
31.5k 22 22 gold badges 109 109 silver badges 132 132 bronze badges
answered Dec 17, 2021 at 13:04
Ihor Verkhohliad Ihor Verkhohliad
61 1 1 silver badge 1 1 bronze badge
If you have lodash you can use its .get method
_.get(a, 'b.c.d.e')
or give it a default value
_.get(a, 'b.c.d.e', default)
answered May 17, 2019 at 19:33
Brandon Dyer Brandon Dyer
1,362 12 12 silver badges 21 21 bronze badges
Hi, can that also capture property of null error?
Commented Apr 7, 2022 at 16:22
I like Cao Shouguang's answer, but I am not fond of passing a function as parameter into the getSafe function each time I do the call. I have modified the getSafe function to accept simple parameters and pure ES5.
/** * Safely get object properties. * @param prop The property of the object to retrieve * @param defaultVal The value returned if the property value does not exist * @returns If property of object exists it is returned, * else the default value is returned. * @example * var myObj = >; * var value; * * value = getSafe(myObj.a.b,'No Value'); //returns c * value = getSafe(myObj.a.x,'No Value'); //returns 'No Value' * * if (getSafe(myObj.a.x, false)) < * console.log('Found') * >else < * console.log('Not Found') * >; //logs 'Not Found' * * if(value = getSafe(myObj.a.b, false)) < * console.log('New Value is', value); //logs 'New Value is c' * >*/ function getSafe(prop, defaultVal) < return function(fn, defaultVal) < try < if (fn() === undefined) < return defaultVal; >else < return fn(); >> catch (e) < return defaultVal; >>(function() , defaultVal); >