JL91
JL91

Reputation: 83

Recursively return array of object and nested object keys

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

Answers (4)

Yoav Kadosh
Yoav Kadosh

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

Mulan
Mulan

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

Nikhil Aggarwal
Nikhil Aggarwal

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

adiga
adiga

Reputation: 35263

  • You need to loop through the object using for...in. The for loop is for arrays
  • obj[i] === typeOf object is not correct. It should be typeof obj[key] === "object".
  • If the nested property is an object, you need to recursively call the function and 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

Related Questions