0

Trying to create a function that returns an array of the keys of an object, including of objects nested within. My code looks like this:

function keyCount(obj, arr) {
  if (arr == undefined){
    arr = []
  }
  let keys = Object.keys(obj);
  let length = keys.length;
  for (i = 0; i <= length; i++){
    if (Object.keys(obj)[i] !== undefined){
      arr.push(Object.keys(obj)[i]);
    }
  }
  for (i = 0; i <= length; i++){
    if (typeof(obj[Object.keys(obj)[i]]) === "object" && obj[Object.keys(obj)[i]] !== null && Object.keys(obj)[i] !== undefined){
      let nestedobj = obj[Object.keys(obj)[i]];
      keyCount(nestedobj,arr)
    }
  }
  return (arr);
}

This returns the first level keys and the keys of one nested object, but exits the function after the first recursive call. Is there a way around this or a better way to format the code? Thanks in advance!

Edited: Data expectations:

let obj1 = {
1:"a",
2:{
  3: "b",
  4: "c"},
5:{
  6:"d",
  7: "e",
  8: {
    9: "f",
    10: "g"},
    11:{
      12:"h",
      13: "i"
      }
    }
  };

Should return:

[1,2,5,3,4,6,7,8,9,10,11,12,13]

But only returns:

[1,2,5,3,4]
7
  • Possible duplicate of How to get all keys with values from nested objects Commented Mar 8, 2018 at 12:38
  • @Durga Here he asks for the keys only, that question has other meaning in question subject. Commented Mar 8, 2018 at 12:40
  • please add some data and the wanted outcome. Commented Mar 8, 2018 at 12:45
  • Check link mentioned by @Durga Commented Mar 8, 2018 at 12:49
  • You really should not repeatedly call Object.keys(obj). That's what you created the let keys variable for! Commented Mar 8, 2018 at 13:03

4 Answers 4

1

You could take the keys and check if the value is an object, then take this keys as well.

function getKeys(object) {
    return Object
        .keys(object)
        .reduce((r, k) => r.concat(k, object[k] && typeof object[k] === 'object' ? getKeys(object[k]) : []), []);
}


var object = { 1: "a", 2: { 3: "b", 4: "c" }, 5: { 6: "d", 7: "e", 8: { 9: "f", 10: "g" }, 11: { 12: "h", 13: "i" } } };

console.log(getKeys(object));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Sign up to request clarification or add additional context in comments.

1 Comment

@georg, right it is guarded by the check object[k] before.
0

There are various problems with your code. The most important is that i is implicitly global, which causes your recursive call to alter the loop condition. You're lucky that you didn't get an infinite loop…

function keyCount(obj, arr) {
  "use strict";
//^^^^^^^^^^^^^ prevent mistakes with forgotten declarations
  if (arr == undefined){
    arr = []
  }
  const keys = Object.keys(obj);
//^^^^^ prefer const over let when you don't assign
  const length = keys.length;
  for (let i = 0; i < length; i++) {
//                 ^^^ don't use <=
//     ^^^ i needs to be a local variable!
    // if (keys[i] !== undefined)
//  ^^^ no longer necessary when having fixed the loop
      arr.push(keys[i]);
//             ^^^^ don't call Object.keys again
  }
  for (let i = 0; i < length; i++) {
//     ^^^          ^ same as above
    const nestedobj = obj[keys[i]]; // move this up here
//                        ^^^^ same as above
    if (typeof nestedobj === "object" && nestedobj !== null  ) {
//            ^         ^ typeof is not a function          ^ same as above
      keyCount(nestedobj, arr);
    }
  }
  return arr;
}

1 Comment

Thank you, this is enormously helpful !
0

let obj1 = {
1:"a",
2:{
  3: "b",
  4: "c"},
5:{
  6:"d",
  7: "e",
  8: {
    9: "f",
    10: "g"},
    11:{
      12:"h",
      13: "i"
      }
    }
  };

var keyArray = [];

function getKeys(obj){
  // get the keys from the current level
  var keys = Object.keys( obj );
  // iterate through each key, add it to our keyArray[]
  for( var i=0, x=keys.length; i<x; i++ ){
    keyArray.push( keys[i] );
    // if there is another array/object (instead of a
    // value) send that section of the obj back into
    // the function
    if( obj[keys[i]].constructor === Object ){
      getKeys( obj[keys[i]] );
    }
  }
}

getKeys(obj1);

//Should return:
//console.log( 'expected', [1,2,5,3,4,6,7,8,9,10,11,12,13] );
console.log( 'result: ', keyArray.join(',') );

1 Comment

@Leafyshark if I understand your comment: Starting with Object.keys(obj1) and then looping over those results will only get the code down two levels (top level and the next one scanned). Using a recursive loop, the code can dig as deep as the object has nested data.
0

Since the answers posted so far don't handle circular objects, here's the one that does:

function getKeys(obj) {
    let seen = new WeakSet(),
        keys = new Set(),
        queue = [obj];

    while (queue.length) {
        let obj = queue.shift();
        
        if (!obj || typeof obj !== 'object' || seen.has(obj))
            continue;
        
        seen.add(obj);
        
        for (let [k, v] of Object.entries(obj)) {
            keys.add(k);
            queue.push(v);
        }
    }

    return [...keys];
}

//

weirdo = {a: 1, b: 2, c: 3}
weirdo.self = weirdo;
console.log(getKeys(weirdo))

And it's not recursive, so it handles more than 10,000 levels of nesting (which is a quite common situation in today's webdev ;)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.