Explain Codes LogoExplain Codes Logo

Test for existence of nested JavaScript object key

javascript
optional-chaining
lodash
best-practices
Nikita BarsukovbyNikita Barsukov·Aug 14, 2024
TLDR

Jumping to the point! Test for a nested key in an object using this function:

const keyExists = (obj, path) => path.split('.').every(key => { // undefined? Ain't nobody got time for that! return (obj = obj?.[key]) !== undefined; }); // Example: const obj = { a: { b: { c: 1 } } }; console.log(keyExists(obj, 'a.b.c')); // true console.log(keyExists(obj, 'a.b.c.d')); // false

This function combines split('.') and every() to go through your keys. obj?.[key] provides the safety net when accessing nested object levels.

Delving into optional chaining and reduce

Unpacking optional chaining

With optional chaining ?., you can probe the value of a nested property without the need to explicitly validate each level. With the advent of ECMAScript 2020, this feature simplifies our lives:

// Read deeply nested property, cautiously! const value = obj?.a?.b?.c; // undefined if no such path exists // Tentatively call method... const result = obj?.a?.b?.myMethod?.(); // undefined if myMethod ain't there!

Embrace functional programming with reduce

Using reduce can help recover the spirit of functional programming when testing nested properties:

const nestedKeyExists = (obj, path) => path.split('.').reduce((acc,key) => acc?.[key], obj) !== undefined;

Refactor with lodash and illustrious patterns

Unsheathe your lodash

Working with nested objects? Lodash helps you dive deep. The _.get method retrieves deep values safely:

const _ = require('lodash'); const value = _.get(obj, 'a.b.c', 'default');

And you can even use array paths for complicated structures:

// Array-based keys, because why not? const value = _.get(obj, ['a', 'b', 'c'], 'default');

Code patterns coming to rescue

The Oliver Steele's pattern, for example, lets you access nested properties in a classy way:

// Oliver Steele's fancy access pattern const value = (((obj || {}).a || {}).b || {}).c;

Embrace the future with babel

To ensure browser compatibility and get your hands on modern JavaScript features, use Babel:

{ "plugins": ["@babel/plugin-proposal-optional-chaining"] }

With Babel 7.8.0 or later, you can use today what will be standard tomorrow.

Evade traps: best practices

Keeping it simple

KISS (Keep It Simple, Stupid)! Avoid overengineering and magic tricks (Reflect.has). Explicit is better than implicit!

Watch out for memory leaks

Preserving references is crucial. Don't create unneeded closures—structure your code wisely.

Cross-browser compatibility

Always check whether your environment supports the feature. Tools like "caniuse" or Babel's compatibility tables can save your day.

Deepening your nested knowledge

Default values to the rescue

Provide a default value when dealing with possible undefined values:

const safeAccess = (obj, path) => path.split('.').reduce((acc, key) => acc ? acc[key] : {}, obj); const defaultValue = safeAccess(obj, 'a.b.c') || 'default';

Mixed keys? Why not!

For paths that include an array index or a variable key, enhance your functions to handle these scenarios:

const isValidPath = (obj, path) => { return path.split('.').reduce((acc, key) => { if (acc && key in acc) return acc[key]; // Arrays be like: "Don't ignore me!" if (Array.isArray(acc) && !isNaN(key)) return acc[key]; return undefined; }, obj); };

Nested but flexible

When dealing with data of changing structure, create an all-weather function that can take up unexpected formats.