23

I have the following object:

var abc = {
    1: "Raggruppamento a 1",
    2: "Raggruppamento a 2",
    3: "Raggruppamento a 3",
    4: "Raggruppamento a 4",
    count: '3',
    counter: {
        count: '3',
    },
    5: {
        test: "Raggruppamento a 1",

        tester: {
            name: "Georgi"
        }
    }
};

I would like to retrieve the following result:

  • abc[1]
  • abc[2]
  • abc[3]
  • abc[4]
  • abc.count
  • abc.counter.count
  • abc[5]
  • abc[5].test
  • abc[5].tester
  • abc[5].tester.name

is that possible using nodejs maybe with the help of plugins?

Georgi Kirilov
  • 569
  • 2
  • 5
  • 12

7 Answers7

31

You can do this by recursively traversing the object:

function getDeepKeys(obj) {
    var keys = [];
    for(var key in obj) {
        keys.push(key);
        if(typeof obj[key] === "object") {
            var subkeys = getDeepKeys(obj[key]);
            keys = keys.concat(subkeys.map(function(subkey) {
                return key + "." + subkey;
            }));
        }
    }
    return keys;
}

Running getDeepKeys(abc) on the object in your question will return the following array:

["1", "2", "3", "4", "5", "5.test", "5.tester", "5.tester.name", "count", "counter", "counter.count"]
Peter Olson
  • 139,199
  • 49
  • 202
  • 242
  • Just found an issue in your example. What happens if they key is a number? example: abc.test[0] - Your example will output abc.test.0 which is wrong. – Georgi Kirilov Mar 10 '17 at 13:15
  • @GeorgiK. JavaScript doesn't distinguish between string keys and numeric keys. `test[0]` and `test["0"]` are the same thing. I'm not sure what purpose you're using this code for, why do you need to make this distinction? – Peter Olson Mar 10 '17 at 15:47
  • Found this answer through google and adjusted it slightly for my personal need with an added if/else and TypeScript types. Sharing it forward for other googlers like myself: https://paste.ee/p/US30f (paste seeing as code in a comment lacks formatting) – Favna Jul 08 '22 at 17:52
8

Smaller version, no side effect, just 1 line in function body:

function objectDeepKeys(obj){
  return Object.keys(obj).filter(key => obj[key] instanceof Object).map(key => objectDeepKeys(obj[key]).map(k => `${key}.${k}`)).reduce((x, y) => x.concat(y), Object.keys(obj))
}

var abc = {
    1: "Raggruppamento a 1",
    2: "Raggruppamento a 2",
    3: "Raggruppamento a 3",
    4: "Raggruppamento a 4",
    count: '3',
    counter: {
        count: '3',
    },
    5: {
        test: "Raggruppamento a 1",

        tester: {
            name: "Ross"
        }
    }
};

function objectDeepKeys(obj){
  return Object.keys(obj)
    .filter(key => obj[key] instanceof Object)
    .map(key => objectDeepKeys(obj[key]).map(k => `${key}.${k}`))
    .reduce((x, y) => x.concat(y), Object.keys(obj))
}

console.log(objectDeepKeys(abc))
ronald8192
  • 5,003
  • 2
  • 14
  • 23
6

I know this is bit old post...

This code covers all criteria in JSON object format like just object,object array, nested array object,nested object with array object etc.

getDeepKeys = function (obj) {
  var keys = [];
    for(var key in obj) {
        if(typeof obj[key] === "object" && !Array.isArray(obj[key])) {
            var subkeys = getDeepKeys(obj[key]);
            keys = keys.concat(subkeys.map(function(subkey) {
                return key + "." + subkey;
            }));
        } else if(Array.isArray(obj[key])) {
            for(var i=0;i<obj[key].length;i++){
               var subkeys = getDeepKeys(obj[key][i]);
               keys = keys.concat(subkeys.map(function(subkey) {
                return key + "[" + i + "]" + "." + subkey;
               }));
            }
        } else {
          keys.push(key);
        }
    }
    return keys;
}
Dhatri Suresh
  • 811
  • 7
  • 5
  • Thanks! This is exactly what I was looking for. I like this because, unlike the accepted answer, it returns the deepest keys instead of all keys: [ '1', '2', '3', '4', '5.test', '5.tester.name', 'count', 'counter.count', ] – lwdthe1 Jul 30 '19 at 23:33
3

Consider an implementation of deepKeys using functional style. We can avoid headaches of mutation, variable reassignment, intermediate assignments, and other side effects -

  1. If the input t is an object, for each (k,v) pair in the object, append k to the path and recur on the sub-problem, v
  2. (induction) the input is not an object. return the formatted path

We can encode this as follows -

const deepKeys = (t, path = []) =>
  Object(t) === t
    ? Object                                             // 1
        .entries(t)
        .flatMap(([k,v]) => deepKeys(v, [...path, k]))
    : [ path.join(".") ]                                 // 2

const input =
  {1:"Raggruppamento a 1",2:"Raggruppamento a 2",3:"Raggruppamento a 3",4:"Raggruppamento a 4",count:'3',counter:{count:'3',},5:{test:"Raggruppamento a 1",tester:{name:"Georgi"}}}

for (const path of deepKeys(input))
  console.log(path)

Another great choice to implement this program is JavaScript's generators. Notice the similarity between this deepKeys and the implementation above. They both effectively do the same thing -

function* deepKeys (t, path = [])
{ switch(t?.constructor)
  { case Object:
      for (const [k,v] of Object.entries(t))  // 1
        yield* deepKeys(v, [...path, k])
      break
    default:
      yield path.join(".")                    // 2
  }
}

const input =
  {1:"Raggruppamento a 1",2:"Raggruppamento a 2",3:"Raggruppamento a 3",4:"Raggruppamento a 4",count:'3',counter:{count:'3',},5:{test:"Raggruppamento a 1",tester:{name:"Georgi"}}}

for (const path of deepKeys(input))
  console.log(path)

Output is the same for each variant of deepKeys -

1
2
3
4
5.test
5.tester.name
count
counter.count
Mulan
  • 129,518
  • 31
  • 228
  • 259
1

I used this code(some fix for previously code from 'Peter Olson', used lodash) to get the keys, and check if some of value is Date:

getDeepKeys = function (obj) {
  let keys = [];
  for (let key in Object.keys(obj)) {
    let value = obj[key];
    if (_.isDate(value)) {
      keys.push(key);
    } else if (_.isObject(value)) {
      let subkeys = getDeepKeys(value);
      keys = keys.concat(subkeys.map(function(subkey) {
        return key + "." + subkey;
      }));
    } else {
      keys.push(key)
    }
  }
  return keys;
}

i also checked if value is mongoDBRef used condition like this: ((_.isObject(value)) && (value && value.oid))

atwright147
  • 3,694
  • 4
  • 31
  • 57
Vitalii Andrusishyn
  • 3,984
  • 1
  • 25
  • 31
0
  getDeepKeys = function (obj) {
var keys = [];
  for(var key in obj) {
      if(typeof obj[key] === "object" && !Array.isArray(obj[key])) {
          var subkeys = getDeepKeys(obj[key]);
          keys = keys.concat(subkeys.map(function(subkey) {
              return key + "." + subkey;
          }));
      } else if(Array.isArray(obj[key])) {
          for(var i=0;i<obj[key].length;i++){
              if ( typeof (obj[key][i]) == "string") {
                 console.log(obj[key][i])
                 keys.push(key)
              }
              else{
                   var subkeys = getDeepKeys(obj[key][i]);
             keys = keys.concat(subkeys.map(function(subkey) {
              return key + "[" + i + "]" + "." + subkey;
             }));
              }

          }
      } else {
        keys.push(key);
      }
  }
  return keys;

}

0

Using recursive function will Help

findKeys = (obj, p) => {

var parent = p
for (let i of Object.keys(obj)) {

  if (typeof (obj[i]) === "object") {
    var k = p + "." + i
    console.log(k)
    this.findKeys(obj[i], k)
  } else {
    console.log(parent + "." + i)
  }
}

 findKeys(abc,"abc")`