JavaScript Find Path of Key in Deeply Nested Object or Array

Key Path Finder Using Depth First Search (DFS)

I'm open to new opportunities! For a full-time role, contract position, or freelance work, reach out at talha@talhaawan.net or LinkedIn.
Deeply nested JavaScript object

The following two codes go through the whole object, an 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, and second gets the path where a key has the given value.

You can use findPaths method of the npm package npm logodeeply-nested for the same functionality.

A similar recursive solution as below can also be used to modify all the keys of a deeply nested object or array of objects.

1. Get Path to the Nested Key

Code

const findPath = (ob, key) => {
  const path = [];
  const keyExists = (obj) => {
    if (
      !obj ||
      typeof key !== "string" ||
      (typeof obj !== "object" && !Array.isArray(obj))
    ) {
      return false;
    } else if (obj.hasOwnProperty(key) && !Array.isArray(obj)) {
      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]);
        if (result) {
          return result;
        }
        path.pop();
      }
    } else {
      for (const k in obj) {
        path.push(k);
        const result = keyExists(obj[k]);
        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 Case

Compare a known path for an object in a single check without needing 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 slight modification, we can make the above code compare the value given for the key.

const findPath = (ob, key, value) => {
  const path = [];
  const keyExists = (obj) => {
    if (
      !obj ||
      typeof key !== "string" ||
      (typeof obj !== "object" && !Array.isArray(obj))
    ) {
      return false;
    } else if (obj.hasOwnProperty(key) && && !Array.isArray(obj) && 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]);
        if (result) {
          return result;
        }
        path.pop();
      }
    } else {
      for (const k in obj) {
        path.push(k);
        const result = keyExists(obj[k]);
        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 the above codes is that they only return the first path found for a given key or key/value pair.




See also

When you purchase through links on techighness.com, I may earn an affiliate commission.