4 Ways to Safely Access Nested Objects in Vanilla Javascript

How to access nested objects and write safer code

Zeng Hou Lim
Sep 7 · 4 min read
Photo by Blake Connally on Unsplash

If you’re working with Javascript, chances are you might have encountered a situation where you have had to access a deeply nested object. If everything goes well, you get your data without any problems. That’s great!

However, in reality, we are not always guaranteed that the data exists, but mistakenly assume that it does. I have been in that position multiple times and I often find myself thinking: “How could that value not be populated?”

Thankfully, all we need to prevent these pesky errors is an additional check for undefined values.


Example of the Error

const macAyres = {
tours: {
nearMe: {
sanFrancisco: {
date: 'Sun Oct 27',
location: 'The Regency Ballroom',
cost: '30.00',
},
},
}
}

If I had mistakenly assumed that this concert was in San Jose and wanted to retrieve the location of the (imaginary) concert, I might have typed something like this:

const concertLocation = macAyres.tours.nearMe.sanJose.location;

As the property sanJose does not exist, the expression above will be akin to something like undefined.location. Of course, we’ll be greeted with an all too familiar error; uncaught typeerror: cannot read property ‘type’ of undefined.


1. Ternary Operator to Check for null/undefined

const concertCity = macAyres.tours.nearMe.sanJose
const concertLocation = concertCity ? concertCity.location : undefined;

This option is possibly the easiest to implement and check. When the object is not deeply nested, this could be a great way to check.

However, when we have to traverse several layers deeper, the checks can get pretty gnarly and repetitive.

const concertLocation = (macAyres.tours &&
macAyres.tours.nearMe &&
macAyres.tours.nearMe.sanJose) ? macAyres.tours.nearMe.sanJose.location : undefined;

If you have a deeply nested object, the last two ways provide a more elegant way.


2. Oliver Steele’s Nested Object Access Pattern

Instead of using the && operator to check for null/undefined values, we use the || operator and an empty object literal. The example below will provide more clarity.

const concertLocation = (macAyres.tours.nearMe.sanJose || {}).location;

As the || operator breaks return the truthy value encountered, the above expression would return macAyres.tours.nearMe.sanJose if it is not null/undefined, otherwise {}.

In this case, we will be accessing location from an empty object, and not from undefined, so we will avoid getting the error.

Do also note that, as the || operator looks for the first encountered truthy value, we should not write an expression like this:

const concertLocation = ({} || macAyres.tours.nearMe.sanJose).location;

In this case, we will always fall back onto {} as it is considered a
truthy value. If you would like to learn more about logical operators, this article provides an excellent rundown.

Similar to the method described above, even Oliver Steele’s pattern can get increasingly unreadable with all the parenthesis if we needed to use || operators at every step of the way.

Fortunately, the next method shows how you can leverage a powerful JavaScript function to keep things clean.


3. Array Reduce

In short, reduce executes a reducer function on an array and returns a single output. You can imagine that the array we’d be working with would be an array that contains, in sequence, the path that we’d traverse through the object.

We will use the above example data structure but, to demonstrate the capability of the reduce function, we will assume that each level of data is not guaranteed.

// Data Structure for recap
const macAyres = {
tours: {
nearMe: {
sanFrancisco: {
date: 'Sun Oct 27',
location: 'The Regency Ballroom',
cost: '30.00',
},
},
}
}
const paths = ['tours', 'nearMe', 'sanJose', 'location'];
const location = paths.reduce((object, path) => {
return (object || {})[path]; // Oliver Steele's pattern
}, macAyres)

Notice how we incorporated what we have learned earlier! If you prefer not to use the Oliver Steele pattern, the ternary operator check works fine too.

This way of accessing values is one of my personal favorites as it remains highly readable and DRY, even if the object is deeply nested.


4. Try/Catch Helper Function With ES6 Arrow Function

I have not used this before but this neat way of doing this can be implemented as a utility method and be more reusable. After writing this point, I suspect that I might really like this the most.

// Code Snippet taken from the post
function getSafe(fn, defaultVal) {
try {
return fn();
} catch (e) {
return defaultVal;
}
}

// use it like this
getSafe(() => obj.a.lot.of.properties);

// or add an optional default value
getSafe(() => obj.a.lot.of.properties, 'nothing');

Conclusion

Although there are libraries that can help you overcome that, you might want to consider these plain JavaScript methods if you want a simple and fast way.

On a final note, I hope you picked up something useful from this article. This is my first time writing a technical piece and I hope people find value in it.

Better Programming

Advice for programmers.

Zeng Hou Lim

Written by

Software Engineer at LeanData. Excited about living my best life and becoming a better engineer. I like taking complex ideas and breaking them down.

Better Programming

Advice for programmers.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade