Reputation: 83
I am learning about recursion at the moment and have moved on from numbers, string and arrays into using it on objects... I'm trying to work out the best method for taking an object as an argument and collecting the keys of the object and all nested objects into an array
I can return the object keys of a single object not using recursion. So i was trying to create a variable as an empty array then iterate over the object using a for loop and if "i" is an object then push object keys into the array variable and return it. This wouldnt work unfortunate.
I would like the following:
{lamp: 2, candle: 2, pillow: {big: 2, small: 4}, bathroom: {toilet: 1, shower: {shampoo: 1, conditioner: 2}}}
To return:
[lamp, candle, pillow, big, small, bathroom, toilet, shower, shampoo, conditioner]
Hope this explains enough, let me know if not :)
I tried the following:
function(obj) {
let keysArray = [];
for (let i = 0, i < obj.length, i++)
if (obj[i] === typeOf object) {
keysArray.push(obj[i].keys);
}
return keysArray
}
Upvotes: 0
Views: 1484
Reputation: 5165
How about:
const keys = obj => Object.keys(obj).reduce((acc, key) => {
acc.push(key);
return (obj[key] !== null && typeof obj[key] === 'object') // Avoid evaluating null as an object
? acc.concat(keys(obj[key]))
: acc;
}, []);
Usage:
keys({foo: 1, bar: {foobar: 2}}); // Outputs ['foo', 'bar', 'foobar']
Upvotes: 1
Reputation: 135406
A very good use-case for generators. Here's a demonstration -
const data =
{lamp: 2, candle: 2, pillow: {big: 2, small: 4}, bathroom: {toilet: 1, shower: {shampoo: 1, conditioner: 2}}}
const keys = function* (o = {}) {
if (Object(o) === o)
for (const [k, v] of Object.entries(o)) {
yield k
yield* keys(v)
}
}
console.log(Array.from(keys(data)))
// [lamp, candle, pillow, big, small, bathroom, toilet, shower, shampoo, conditioner]
Another solution is to use Array.prototype.flatMap
-
const data =
{lamp: 2, candle: 2, pillow: {big: 2, small: 4}, bathroom: {toilet: 1, shower: {shampoo: 1, conditioner: 2}}}
const keys = (o = {}) =>
Object(o) === o
? Object.entries(o).flatMap(([ k, v ]) =>
[ k, ...keys(v) ]
)
: []
console.log(keys(data))
// [lamp, candle, pillow, big, small, bathroom, toilet, shower, shampoo, conditioner]
Upvotes: 0
Reputation: 28475
You can write a recursive function as follows
let obj = {lamp: 2, candle: 2, pillow: {big: 2, small: 4}, bathroom: {toilet: 1, shower: {shampoo: 1, conditioner: 2}}};
function getKeys(o) {
let result = [];
for (let key in o) {
result.push(key);
if(o[key] && typeof o[key] === "object") result.push(...getKeys(o[key]));
}
return result;
}
console.log(getKeys(obj));
Upvotes: 3
Reputation: 35263
for...in
. The for
loop is for arraysobj[i] === typeOf object
is not correct. It should be typeof obj[key] === "object"
. push
keys to keysArray
function getKeys(obj) {
let keysArray = [];
for (let key in obj) {
keysArray.push(key);
if (typeof obj[key] === "object")
keysArray.push(...getKeys(obj[key]))
}
return keysArray
}
const input={lamp:2,candle:2,pillow:{big:2,small:4},bathroom:{toilet:1,shower:{shampoo:1,conditioner:2}}}
console.log(getKeys(input))
FYI: typeof null
is "object". So, the above code will throw an error if any of the properties are null. So, Object(obj[k]) === obj[k]
can be used. This is true for all objects EXCEPT for null
Also, if flatMap
is supported, you could do something like this
const input={lamp:2,candle:2,pillow:{big:2,small:4},bathroom:{toilet:1,shower:{shampoo:1,conditioner:2}}};
const getKeys = obj =>
Object.keys(obj).flatMap(key => Object(obj[key]) === obj[key]
? [key, ...getKeys(obj[key])]
: key)
console.log(getKeys(input))
Upvotes: 1