The following two codes go through the whole object, array of objects or a collection of both to get the path to a particular key. There are two versions: first gets the path to the key only, second gets the path where a key has the given value.
1. Get Path To The Nested Key
Code
const findPath = (ob, key) => {
const path = [];
const keyExists = (obj) => {
if (!obj || (typeof obj !== "object" && !Array.isArray(obj))) {
return false;
}
else if (obj.hasOwnProperty(key)) {
return true;
}
else if (Array.isArray(obj)) {
let parentKey = path.length ? path.pop() : "";
for (let i = 0; i < obj.length; i++) {
path.push(`${parentKey}[${i}]`);
const result = keyExists(obj[i], key);
if (result) {
return result;
}
path.pop();
}
}
else {
for (const k in obj) {
path.push(k);
const result = keyExists(obj[k], key);
if (result) {
return result;
}
path.pop();
}
}
return false;
};
keyExists(ob);
return path.join(".");
}
Examples
const deeplyNestedObj = {
a: {
b: {
c: {
d: {
e: "e",
f: "f",
g: {
G: undefined,
h: {
i: {},
j: {
k: {
K: null,
l: {
abc: 123,
}
}
}
}
}
}
}
}
},
e1: {},
f2: {},
P1: {
q1: {
r1: "r1"
}
}
}
console.log(findPath(deeplyNestedObj, "K")); // a.b.c.d.g.h.j.k
console.log(findPath(deeplyNestedObj, "r1")); // P1.q1
console.log(findPath(deeplyNestedObj, "gibberish")); // ""
console.log(findPath(deeplyNestedObj, "helloWorld")); // ""
Use
Compare a known path for an object in a single check, without the need of multiple &&
comparisons or a try catch block. Example:
const errorResponse = {
status: 403,
data: {
user: {
errorMessage: "You're not authorized to update this user"
}
}
};
if(findPath(errorResponse, "errorMessage") === "data.user"){ // true
//...
}
2. Get Path To The Nested Key With Given Value
With little modification, the above code can be made to compare the value given for the key.
const findPath = (ob, key, value) => {
const path = [];
const keyExists = (obj) => {
if (!obj || (typeof obj !== "object" && !Array.isArray(obj))) {
return false;
}
else if (obj.hasOwnProperty(key) && obj[key] === value) {
return true;
}
else if (Array.isArray(obj)) {
let parentKey = path.length ? path.pop() : "";
for (let i = 0; i < obj.length; i++) {
path.push(`${parentKey}[${i}]`);
const result = keyExists(obj[i], key);
if (result) {
return result;
}
path.pop();
}
}
else {
for (const k in obj) {
path.push(k);
const result = keyExists(obj[k], key);
if (result) {
return result;
}
path.pop();
}
}
return false;
};
keyExists(ob);
return path.join(".");
}
const deeplyNestedFamilyTree = [ // starts with array
{
name: "John",
children: [{
name: "Dora",
children: [{
name: "Sara"
}, {
name: "James"
}]
}]
},
{
name: "Scott",
children: [{
name: "Smith",
children: [{
name: "Brad"
}, {
name: "David"
}]
}]
}
]
const deeplyNestedOrgChart = { // starts with object
position: "CEO",
subordinates: [
{
position: "CFO"
},
{
position: "CTO",
subordinates: [
{
position: "Engineering Lead"
}
]
},
{
position: "COO"
}
]
}
console.log(findPath(deeplyNestedFamilyTree, "name", "John")); // [0]
console.log(findPath(deeplyNestedFamilyTree, "name", "James")); // [0].children[0].children[1]
console.log(findPath(deeplyNestedFamilyTree, "name", "Scott")); // [1]
console.log(findPath(deeplyNestedOrgChart, "position", "Engineering Lead")); //subordinates[1].subordinates[0]
Limitation
The limitation of both the above codes is that they only return the first path found for a given key or key/value.
See also
- JavaScript: Change The Behavior of A Class Method At Runtime
- JavaScript Rollbar Unknown Unhandled Rejection Error Getting Reason From Event
- JavaScript Disable console.log On Production Environment
- How To Publish And Use A Private JavaScript Library Without NPM Registry?
- JavaScript Recursion With Default Function Parameter
- What Is Destructuring And Restructuring Design Pattern In JavaScript?
- JavaScript: Difference Between Module, Library, Package, API, Framework And Application