Explain Codes LogoExplain Codes Logo

Accessing nested JavaScript objects and arrays by string path

javascript
prompt-engineering
functions
callbacks
Anton ShumikhinbyAnton Shumikhin·Sep 7, 2024
TLDR

To extract nested properties swiftly, here's a one-line magic function:

const getNestedValue = (obj, path) => path.split('.').reduce((o, p) => o?.[p], obj);

Quick demo:

const data = { a: { b: { c: 1 } } }; console.log(getNestedValue(data, 'a.b.c')); // Outputs: 1, faster than a cheetah!

The brilliance of optional chaining (?.) comes into play here, it insulates against unpleasant undefined surprises.

Enhanced retrieval with error handling

When dealing with tricky data, expecting the unexpected is a survival strategy. The try/catch block helps tame the chaos:

const safelyGetNestedValue = (obj, path) => { try { return path.split('.').reduce((o, p) => o?.[p], obj); } catch (error) { console.error(`Bubblegum! Failed to traverse ${path} in the object`, error); return null; // or any default fallback your heart desires } };

When a property is a no-show, this function coolly hands over null or a default, and your program jokingly continues execution.

To boldly go where dot notation cannot (I'm looking at you, special characters), we modify the string paths:

const getNestedValueWithBrackets = (obj, path) => { const reg = /\.|\[(\d+)\]/g; // Match dot or index in brackets const pList = path.replace(reg, (match, p1) => `.${p1}`).split('.'); return pList.reduce((o, p) => o?.[p], obj); }; const data = { a: [{ 'b.c': { d: 2 } }, 3] }; console.log(getNestedValueWithBrackets(data, 'a[0].b\\.c.d') // Outputs: 2, wasn't that weirdly fun?

It can handle numeric array indices and keys using periods, broadening your path resolution power.

Crafting custom setters and default values

Putting the right things in the right places - that's what good setters do. Here's one that tags along with default value generation:

const setNestedValue = (obj, path, value, defaultValue=null) => { const pList = path.split('.'); const length = pList.length; pList.reduce((o, p, index) => { if (index === length - 1) { o[p] = value; return value; } o[p] = o[p] || defaultValue; return o[p]; }, obj); }; setNestedValue(data, 'a.1.deep.path', 4, {}); console.log(data.a[1]); // Outputs: { deep: { path: 4 } }, presto! We manipulated reality (kinda).

This fella creates anyone or anything that's amiss during a walk-through across your object.

Lodash to the rescue

Expanding your toolset, lodash lends us the fabulous _.get() function that simplifies these nested missions. It supports array indexing and conveniently provides default values:

const _ = require('lodash'); const data = { a: { b: [1, { c: 3 }] } }; console.log(_.get(data, 'a.b[1].c', 'default')); // Outputs: 3, thank you lodash!

Lodash is a trusty companion when navigating through complex data in production-grade code.

Leverage modern JavaScript

Modern JavaScript offers nifty things for enhanced code crafting. Consider destructuring assignment for extracting nested variables, or rest operator for grabbing substructures:

const { a: { b: [firstItem, { c }] } } = data; console.log(c); // Outputs: 3, Swish and flick (the JS wand) for quick data access.

While these beautify your code, remember they might not solve all your dynamic path strings puzzles.