- 4 Ways to Safely Access Nested Objects in Vanilla Javascript
- Example of the Error
- 1. Ternary Operator to Check for null/undefined
- Вложенные объекты — JS: Объекты
- Печать на экран
- Проверки в глубину
- Оператор опциональной последовательности
- Оператор нулевого слияния
- get (lodash)
- Открыть доступ
- How to Test for Existence of Nested JavaScript Object Key?
- Write Our Own Function
- Recursively Check for a Given Property
- Optional Chaining Operator
- Conclusion
- Accessing Nested Objects in JavaScript
- Oliver Steele’s Nested Object Access Pattern
- Access Nested Objects Using Array Reduce
- Typy
4 Ways to Safely Access Nested Objects in Vanilla Javascript
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
For those folks who are fortunate to not have seen this before, here’s an example to get you acquainted. Let’s say I have a JavaScript object, representing a music artist that I am interested in.
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
The most straightforward way to deal with that is to check if an object is null/undefined before attempting to access the data within.
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.
Вложенные объекты — JS: Объекты
Значением свойства объекта может быть всё, что угодно, включая другой объект или массив:
const user = name: 'Vasya', married: true, age: 25 >; // Добавим свойство friends со списком друзей user.friends = ['Kolya', 'Petya']; // Добавим свойство children со списком детей, // каждый ребёнок представлен отдельным объектом user.children = [ name: 'Mila', age: 1 >, name: 'Petr', age: 10 >, ]; // Добавим вложенный объект user.company = name: 'Hexlet' >; console.log(user); // => // // name: 'Vasya', // married: true, // age: 25, // friends: [ 'Kolya', 'Petya' ], // children: [ < name: 'Mila', age: 1 >, < name: 'Petr', age: 10 >], // company: // >
Все то же самое можно определить сразу при создании объекта:
const user = name: 'Vasya', married: true, age: 25, friends: ['Kolya', 'Petya'], children: [ name: 'Mila', age: 1 >, name: 'Petr', age: 10 >, ], company: name: 'Hexlet' >, >;
В этом случае обращение к вложенным элементам происходит по цепочке:
user.friends[1]; // 'Petya' user.children[0].name; // 'Mila' user.company.name; // 'Hexlet' // Или через квадратные скобки user['children'][0]['name']; // 'Mila'
Печать на экран
В console.log() встроено одно ограничение. Если в объекте есть другие объекты на глубине больше второго уровня вложенности, то при выводе такого объекта на экран вместо объектов отобразится строка [Object] , а вместо массива — [Array] .
const obj = a: b: c: key: 'value' >, e: [1, 2] > > >; console.log(obj); // < a: < b: < c: [Object], e: [Array] >> >
Для вывода таких объектов можно воспользоваться функцией преобразования в JSON:
console.log(JSON.stringify(obj)); // ,"e":[1,2]>>> // Или форматированный вывод console.log(JSON.stringify(obj, null, ' ')); // // "a": // "b": // "c": // "key": "value" // >, // "e": [ // 1, // 2 // ] // > // > // >
Проверки в глубину
При работе с вложенными объектами резко усложняется задача проверки существования ключей. Приходится строить цепочку из условий до нужного свойства. Представьте, что нам нужно добраться до 4 уровня вложенности и мы не уверены в том, что существуют все промежуточные объекты:
// Добираемся до obj.one.two.three if (Object.hasOwn(obj, 'one')) if (Object.hasOwn(obj.one, 'two')) if (Object.hasOwn(obj.one.two, 'three')) // . > > >
Так будет выглядеть решение в лоб. Однако, есть более удобный способ, речь о котором ниже.
Оператор опциональной последовательности
Если задача состоит в том, чтобы извлечь данные, а не просто проверить их существование, то можно пойти другим путем. В Javascript встроен оператор опциональной последовательности (optional chaining), который позволяет извлекать вложенные данные без проверок:
const obj = <>; obj?.one?.two?.three // undefined
Этот оператор никогда не приводит к ошибке. Он работает на любых типах данных и всегда возвращает либо undefined , либо значение указанного свойства, если оно существует.
Оператор нулевого слияния
С помощью оператора нулевого слияния, можно не только получить значение цепочки любой вложенности, но и определить значение по умолчанию для него.
const obj = <>; obj?.one?.two?.three ?? 'defaultValue' // 'defaultValue'
Значение по умолчанию возвращается только в том случае, когда слева undefined или null . В этом смысле данный оператор совсем не похож на логическое сравнение || :
const value = false; value ?? 'default'; // false value || 'default'; // 'default'
get (lodash)
Пример выше перегружен символами и выглядит достаточно сложно. Как альтернативу можно использовать функцию get() библиотеки Lodash.
import _ from 'lodash'; const obj = <>; const value = _.get(obj, 'one.two.three', 'defaultValue'); // 'defaultValue'
get() особенно удобен в случае динамических ключей. В таком случае вторым аргументом можно передать массив ключей:
Открыть доступ
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно
- 130 курсов, 2000+ часов теории
- 1000 практических заданий в браузере
- 360 000 студентов
Наши выпускники работают в компаниях:
How to Test for Existence of Nested JavaScript Object Key?
JavaScript objects are often nested in other objects.
Therefore, we may have to check if a nested property exists in an object.
In this article, we’ll look at how to check if a nested property exists in an object.
Write Our Own Function
We can write our own JavaScript function to check if a deeply nested property exists in an object.
For instance, we can write:
const checkNested = (obj, . props) => < for (const prop of props) < if (!obj || !Object.prototype.hasOwnProperty.call(obj, prop)) < return false; >obj = obj[prop]; > return true; > const obj = < level1: < level2: < level3: 'level3' >> >; console.log(checkNested(obj, 'level1', 'level2', 'level3')); console.log(checkNested(obj, 'level1', 'level2', 'bar'));
We create the checkNested function with the obj parameter and the props rest parameters.
obj is the object we’re checking.
props has an array of properties that forms the path to the nested property we’re looking for.
In the function, we loop through the props array to traverse obj to find the nested property.
To do that, we check if obj is falsy or if hasOwnProperty returns false .
If either of them are true , then we know the property doesn’t exist.
We call hasOwnProperty with Object.prototype.hasOwnProperty.call instead of obj.hasOwnProperty since obj.hasOwnProperty can be overridden.
call takes the this value for hasOwnProperty .
prop is the property name string value.
If we get past the if block, then we assign obj to obj[prop] to look one level deeper.
We can do that since the property is an object.
If the whole loop is iterated through successfully, then we return true since the path leads to the property we’re looking for.
We test this function with the obj object, and we can see the first console returns true .
And the 2nd one returns false as expected.
Recursively Check for a Given Property
We can also do the check with a recursive function.
For instance, we can write:
const checkNested = (obj, prop, . restProps) => < if (obj === undefined) < return false; >if ( restProps.length === 0 && Object.prototype.hasOwnProperty.call(obj, prop) ) < return true; >return checkNested(obj[prop], . restProps); >; const obj = < level1: < level2: < level3: "level3" >> >; console.log(checkNested(obj, "level1", "level2", "level3")); console.log(checkNested(obj, "level1", "level2", "foo"));
This checkNested function is similar to the one we have before.
The difference is that we get the first property from the path we’re looking for.
And the rest stays in the array.
We check if restProp.length is 0 and if obj has the prop property.
If they’re both true , then we return true since we traversed the whole path and we found the property.
If they’re anything left in restProps , then we call checkNested to drill down one level lower.
And we should get the same result as before.
Optional Chaining Operator
Another way to check if a nested property path exists is to use tyhe optional chaining operator, which is added with ?. .
This operator is new to ES2020.
For instance, we can write:
const obj = < level1: < level2: < level3: "level3" >> >; const value1 = obj?.level1?.level2?.level3; const value2 = obj?.level1?.level2?.foo; console.log(value1); console.log(value2);
We should see that value1 is ‘level3’ and value2 is undefined since the operator returns the property value if the property exists and undefined otherwise.
This lets us reduce the amount of work needed to traverse deeply nested properties safely.
Conclusion
We can test for the existence of a deeply nested JavaScript object property with our own code or the optional chaining operator.
Accessing Nested Objects in JavaScript
tldr; safely access nested objects in JavaScript in a super cool way. JavaScript is amazing, we all know that already. But a few things in JavaScript are really weird and they make us scratch our heads a lot. One of those things is the confrontation with this error when you try to access a nested object, Cannot read property ‘foo’ of undefined Most of the times when we’re working with JavaScript, we’ll be dealing with nested objects and often we’ll be needing to access the innermost nested values safely. Let’s take this nested object as an example.
const user = id: 101, email: 'jack@dev.com', personalInfo: name: 'Jack', address: line1: 'westwish st', line2: 'washmasher', city: 'wallas', state: 'WX' > > >
const name = user.personalInfo.name; const userCity = user.personalInfo.address.city;
This is easy and straight-forward. But, for some reason, if our user’s personal info is not available, the object structure will be like this,
const user = id: 101, email: 'jack@dev.com' >
const name = user.personalInfo.name; // Cannot read property 'name' of undefined
This is because we’re trying to access name key from an object that does not exist. The usual way how most devs deal with this scenario is,
const name = user && user.personalInfo ? user.personalInfo.name : null; // undefined error will NOT be thrown as we check for existence before access
This is okay if your nested structure is simple, but if you have your data nested 5 or 6 levels deep, then your code will look really messy like this,
Oliver Steele’s Nested Object Access Pattern
This is my personal favorite as it makes the code look clean and simple. I picked this style from stackoverflow a while back and it is pretty catchy once you understand how it works.
const name = ((user || <>).personalInfo || <>).name;
With this notation, you’ll never run into Cannot read property ‘name’ of undefined. You basically check if user exists, if not, you create an empty object on the fly. This way, the next level key will always be accessed from an object that exists or an empty object, but never from undefined. Unfortunately, you cannot access nested arrays with this trick
Access Nested Objects Using Array Reduce
const getNestedObject = (nestedObj, pathArr) => return pathArr.reduce((obj, key) => (obj && obj[key] !== 'undefined') ? obj[key] : undefined, nestedObj); > // pass in your object structure as array elements const name = getNestedObject(user, ['personalInfo', 'name']); // to access nested array, just pass in array index as an element the path array. const city = getNestedObject(user, ['personalInfo', 'addresses', 0, 'city']); // this will return the city from the first address item.
Typy
If you think the above methods are a lil’ too mainstream, then you should try Typy library that I’ve written. In addition to safely accessing nested objects, it does many more awesome things. 🎉 It is available as an npm package — Typy If you use Typy, your code will look like this,
import t from 'typy'; const name = t(user, 'personalInfo.name').safeObject; const city = t(user, 'personalInfo.addresses[0].city').safeObject; // address is an array
Edit: There a few other libraries like Lodash and Ramda that can do this. But in light-weight front-end projects, especially if you’re going to need only one or two methods from those libs, it’s a good idea to opt for an alternative light-weight lib, or better, write your own. Happy ‘safely accessing nested objects in JavaScript’! 💥