This is our recursive function. It takes an object
as an argument and returns all its keys
and all the keys of its nested children, regardless of depth.
function getAllObjectKeys(obj) {
let keys = [];
for (let key in obj) {
keys.push(key);
const val = obj[key];
const valIsObject = typeof(val) === "object";
const valIsNotNull = val !== null;
const valIsNotArray = !Array.isArray(val);
if (valIsObject && valIsNotNull && valIsNotArray) {
const _keys = getAllObjectKeys(val);
keys = keys.concat(_keys);
}
}
return keys
}
This is a deeply nested object:
// Main object
const input = {
a: 'b',
c: 'd',
// Level 1 nested object
e: {
f: 'g'
},
// Level 1 nested object
h: {
// Level 2 nested object
i: {
j: 'k',
l: 'm',
// Level 3 nested object
n: {
o: [1,2,3,4]
}
}
},
p: null
};
Now, we call our function with our nested object and we get all the keys back:
const allObjectKeys = getAllObjectKeys(input);
// ["a", "c", "e", "f", "h", "i", "j", "l", "n", "o", "p"]
This is how it works:
At the beginning of our function, we create an empty array called keys
. This will hold all the keys we find.
At the end of the function, we return this array with all the results.
In order to get the keys, we simply loop through each one of the keys in the provided object (input
) with a for...in
loop and add each one of these keys to our results array:
for (let key in obj) {
keys.push(key);
}
If we run this, we'll get all the keys from the provided object in the first level, and our results would look like this:
keys = ['a', 'c', 'e', 'h', 'p']
However, we are required to return all keys from the nested objects. So the next thing we do is get the value of each of the keys we're iterating over, and check if it is a (nested) object. However, there are some gotchas. In Javascript, both null
and Array
are also of type "object".
typeof([1,2,3,4]) === "object" // true
typeof(null) === "object" // true
These are also objects in Javascript, but they are not nested objects, meaning they are not key/value pairs. Therefore, they are not the kind of objects we are looking for, since we are only interested in getting keys from nested objects. So, assign these conditions and we check they are all present. With this, we can now get the (level 1) nested objects in our provided object:
const val = obj[key]; // We get the value of the current key in the object
const valIsObject = typeof(val) === "object"; // Check if the value is an object type
const valIsNotNull = val !== null; // Check is not a null object type
const valIsNotArray = !Array.isArray(val); // Check is not an Array object type
// If all of the above conditions are met, then we have a "nested" object as a value in the current key:
if (valIsObject && valIsNotNull && valIsNotArray) {
// val (the current obj[key]) is a key/value (nested) object
}
Now that we can identify the nested objects in our provided object, we need to do two things: (1) get all the keys from this nested object and (2) check if there is another (level 2) nested object inside this nested object. Sounds familiar? That's exactly what our function is already doing. Since this nested objects could in turn have other nested objects and these others with n
levels of depth, this is the perfect case for using recursion.
Recursion is a function that calls itself n
number of times. This is what we do in the next line: we call the same function getAllObjectKeys
, and provide as an argument the current key value, which is an object. The result of this function, as we know, is an array of keys from the provided object. So we assign the result to a variable and we merge it to our initial results array. Now we have the keys from the initial object plus the keys from the (level 1) nested object.
if (valIsObject && valIsNotNull && valIsNotArray) {
const _keys = getAllObjectKeys(val);
keys = keys.concat(_keys);
}
Since this is a recursive function, if the provided (level 1) object also has another nested (level 2) object, then our function will be called again and merge those results back, again and again and again. No matter how deep the object is, we will get all the keys from all these objects. In order to make it more clear, we are going to go through the function step by step, so you can see how it all works together.