Reputation: 49
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]
Upvotes: 0
Views: 233
Reputation: 214949
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 ;)
Upvotes: 0
Reputation: 1515
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(',') );
Upvotes: 0
Reputation: 664196
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;
}
Upvotes: 0
Reputation: 386520
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; }
Upvotes: 1