How to Dynamically Access All Nested Object Keys and Values in JS
A developer in a community I participate in recently asked: “How can I access all keys and values in a nested object dynamically? I have data coming from an API where the nesting depth changes, and I need to extract every single key-pair value regardless of how deep it is.”
This is a classic “tree traversal” problem. Because JavaScript objects can contain other objects (or arrays), a simple for...in loop only scratches the surface. To reach the bottom of the nesting, you need a strategy that handles unknown depths.
The Immediate Fix: Recursive Traversal
Section titled “The Immediate Fix: Recursive Traversal”The most reliable way to handle dynamic nesting is a recursive function. This function calls itself whenever it encounters another object.
Version: ES6+ (Node 20 / Modern Browsers)
const userData = { id: 1, profile: { name: "Jane Doe", settings: { theme: "dark", notifications: { email: true, sms: false } } }};
/** * Iterates through all levels of an object and logs key/value pairs. */function walkObject(obj) { for (let key in obj) { if (obj.hasOwnProperty(key)) { const value = obj[key];
// Check if the value is an object and not null (since typeof null is 'object') if (typeof value === 'object' && value !== null) { walkObject(value); // Recursive call } else { console.log(`${key}: ${value}`); } } }}
walkObject(userData);Detailed Explanation: How It Works
Section titled “Detailed Explanation: How It Works”- The Base Case: In recursion, you need an “exit” condition. Here, the loop naturally finishes when it hits a “primitive” value (string, number, boolean, or null).
typeof value === 'object': This is the recursive trigger. If the current value is another container (object or array), we dive deeper by calling the function again with that sub-container.hasOwnProperty: This ensures we are only looking at the keys defined on the object itself, not properties inherited from the JavaScript prototype chain.
Alternative Solution: Flattening the Object
Section titled “Alternative Solution: Flattening the Object”Sometimes you don’t just want to log the values; you want to transform the nested object into a single-level object (a “flat” object) where keys represent the path (e.g., profile.settings.theme).
Version: ES2019+ (using Object.entries)
/** * Flattens a nested object into a single level with dot-notation keys. * illustrative example — verify in your environment */function flattenObject(obj, prefix = '') { return Object.entries(obj).reduce((acc, [key, value]) => { const newKey = prefix ? `${prefix}.${key}` : key;
if (value && typeof value === 'object' && !Array.isArray(value)) { Object.assign(acc, flattenObject(value, newKey)); } else { acc[newKey] = value; }
return acc; }, {});}
const flattened = flattenObject(userData);console.log(flattened);/*Output:{ "id": 1, "profile.name": "Jane Doe", "profile.settings.theme": "dark", "profile.settings.notifications.email": true, "profile.settings.notifications.sms": false}*/Why use Object.entries and reduce?
Section titled “Why use Object.entries and reduce?”- Immutability: Instead of logging to the console, this approach returns a new object, which is better for state management (like in React or Redux).
- Clarity: Dot-notation keys preserve the context of where a value came from, which is vital if two different sub-objects share the same key name (e.g., two different “id” fields).
Edge Cases and Common Pitfalls
Section titled “Edge Cases and Common Pitfalls”1. Circular References
Section titled “1. Circular References”If an object references itself (e.g., obj.self = obj), a recursive function will trigger a RangeError: Maximum call stack size exceeded. If you suspect your data might have circularity, you must use a WeakSet to track visited objects.
2. Arrays as Objects
Section titled “2. Arrays as Objects”In JavaScript, typeof [] is 'object'. If your data contains arrays and you want to treat them as values rather than objects to be traversed, you must add a check using Array.isArray(value).
3. Null Values
Section titled “3. Null Values”Always check value !== null. Since typeof null is 'object', failing to check this will cause your code to try and iterate over null, leading to a TypeError.
Follow-up Questions
Section titled “Follow-up Questions”Can I do this without recursion to save memory?
Yes, you can use a Stack-based iterative approach. Instead of calling the function recursively, you push nested objects onto an array (a stack) and use a while loop to process them. This avoids stack overflow errors on extremely deep objects (e.g., 10,000+ levels deep).
Should I use a library like Lodash for this?
If you are working in a professional production environment with highly complex data, yes. Lodash’s _.get(), _.set(), and _.has() methods handle these edge cases out of the box. However, for most API responses, the native recursive approach shown above is more performant because it avoids the overhead of a large library.
How does this behave with Date objects or RegEx?
Native objects like Date or RegExp will return 'object' from typeof. If you don’t want to traverse into the internal properties of a Date object, you should add a check like !(value instanceof Date) to your recursion condition.