JavaScript Check if Key Exists in Deeply Nested Object or Array of Objects, Without Knowing the Path

Depth First Search (DFS) for Key Verification in an Object

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

The following JavaScript code recursively goes through the whole object, an array of objects, or a collection of both to verify if the given key exists somewhere down the object. It does not require the path to the key in advance.

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

For Python, check the code and example for verifying key exists in deeply nested dictionary and list.

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

Code

const keyExists = (obj, key) => {
  if (
    !obj ||
    typeof key !== "string" ||
    (typeof obj !== "object" && !Array.isArray(obj))
  ) {
    return false;
  } // don't consider array indexes as valid keys
  else if (obj.hasOwnProperty(key) && !Array.isArray(obj)) {
    return true;
  } else if (Array.isArray(obj)) {
    for (let i = 0; i < obj.length; i++) {
      const result = keyExists(obj[i], key);
      if (result) {
        return result;
      }
    }
  } else {
    for (const k in obj) {
      const result = keyExists(obj[k], key);
      if (result) {
        return result;
      }
    }
  }

  return false;
};

Example

const deeplyNestedObj = {
  a: {
    b: {
      c: {
        d: {
          e: "e",
          f: "f",
          g: {
            G: undefined,
            h: {
              i: {},
              j: {
                k: {
                  K: null,
                  l: {
                    abc: 123,
                    m: {
                      n: {
                        o: {
                          p: {
                            q: "q",
                            r: "r",
                            s: "s",
                            u: "u",
                            gibberishObjects: [
                              { gibberishId: 1 },
                              { gibberishId: 2 },
                              "Gibberish String",
                              null,
                            ],
                            aFunction: () => {
                              return true;
                            },
                            anotherDeepObject: {
                              randomDates: {
                                "1999-06-06": {
                                  users: 100,
                                },
                                "2021-jun": "Euro Cup",
                              },
                            },
                          },
                        },
                      },
                    },
                  },
                },
              },
            },
          },
        },
      },
    },
  },
  e1: {},
  f2: {},
  P1: {
    q1: {
      r1: "r1",
    },
  },
};

console.log(keyExists(deeplyNestedObj, "2021-jun")); //true
console.log(keyExists(deeplyNestedObj, "e1")); //true
console.log(keyExists(deeplyNestedObj, "r3")); //false
console.log(keyExists(deeplyNestedObj, "xyz")); //false
console.log(keyExists(deeplyNestedObj, "gibberishId")); //true

Use

Although most of the time we’re aware of the path we need to check for a given key, this method can be used where we’re only interested in the existence of a key. This might be because of the badly-formed JSON structure, or a particular API response. Consider the following example:

const yearlyReport = {
  q1: {
    jan: {
      risk: {
        //...
      },
    },
    feb: {},
    mar: {},
  },
  q2: {
    apr: {},
    may: {},
    jun: {},
  },
  q3: {
    jul: {},
    aug: {},
    sep: {
      bonuses: [
        //...
      ],
    },
  },
  q4: {
    oct: {},
    nov: {
      risk: true,
    },
    dec: {},
  },
};

console.log(keyExists(yearlyReport, "risk")); // true
console.log(keyExists(yearlyReport, "bonuses")); // true
console.log(keyExists(yearlyReport, "holidays")); // false

Here we might only be interested in knowing if the risk, bonuses or holidays key exists somewhere deep down the object, no matter in which quarter or month (assuming their existence means something to us, without knowing their values). For example, we might show on a calendar, without marking the exact quarter and month, that a selected year has planned bonuses, but no holidays, and there’s some risk.

Here’s another example:

const serverResponse = {
  data: {
    availableBookings: {
      hallA: {
        "2021-07-01": true,
        "2021-07-02": true,
        "2021-07-03": true,
        "2021-07-05": true,
        "2021-08-06": true,
        "2021-08-07": true,
        "2021-08-08": {
          //...
        },
      },
      hallB: {
        "2021-07-20": true,
        "2021-07-05": {
          //...
        },
      },
      hallC: {
        section1: {
          "2021-07-02": true,
          "2021-07-15": true,
        },
        section2: {
          "2021-07-22": true,
          "2021-07-25": true,
        },
        section3: {
          "2021-07-12": true,
          "2021-07-18": true,
        },
      },
    },
  },
};

console.log(keyExists(serverResponse, "2021-07-02")); // true
console.log(keyExists(serverResponse, "2021-07-15")); // true
console.log(keyExists(serverResponse, "2021-08-15")); // false
console.log(keyExists(serverResponse, "2021-09-15")); // false
console.log(keyExists(serverResponse, "2021-10-15")); // false

The presence of a date key in a hall or section means it’s available for booking (regardless of its value). We’re not interested in knowing which hall or section has the availability. We can find the booking availability for a particular date across the halls and sections.




See also

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