Benjamin RD
Benjamin RD

Reputation: 12034

Get json values for each level using javascript

I'm trying to use a recursive function to get the last key value form a simple json using javascript

I have this json:

{
  'a': {
    'b': {
      'c': 12,
      'd': 'Hello World'
    },
    'e': [1,2,3]
  }
}

And my expected result is:

{
  'a/b/c': 12,
  'a/b/d': 'Hello World',
  'a/e': [1,2,3]
}

I'm trying with:

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;
} 

But for some reason, it returns me:

a/b/c/d/e/0/1/, I'm not sure why it is adding those numbers there. Someone has an idea about how I can do it?

Upvotes: 2

Views: 232

Answers (3)

ggorlen
ggorlen

Reputation: 57175

You can do it iteratively with an explicit stack which has less overhead than recursion and won't blow the call stack:

const pathify = o => {
  const paths = {};
  const stack = [[o, []]];
  
  while (stack.length) {
    const [curr, path] = stack.pop();
    
    for (const k in curr) {
      if (typeof curr[k] === "object" && !Array.isArray(curr[k])) {
        stack.push([curr[k], path.concat(k)]);
      }
      else {
        paths[`${path.join("/")}/${k}`] = curr[k];
      }
    }
  }
  
  return paths;
};

console.log(pathify({'a':{'b':{'c':12,'d':'Hello World'},'e':[1,2,3]}}));

Upvotes: 4

Mark
Mark

Reputation: 92460

I think you may be making it more complicated than necessary. You can test for an array with Array.isArray and an non-object (typeof !== 'object) and just return the path and value. Otherwise recurse for each entry. reduce() is good for that. Passing the current path as an argument to the recursive function is convenient too:

let obj = {'a': {'b': {'c': 12,'d': 'Hello World'},'e': [1,2,3]}}

function getValues(obj, path = []){
    return (Array.isArray(obj) || typeof obj !== 'object')
    ? {[path.join('/')]: obj}
    : Object.entries(obj).reduce((acc, [key, val]) => 
        Object.assign(acc, getValues(val, path.concat(key)) )
    , {})
    }
    

console.log(getValues(obj))

Upvotes: 3

Ele
Ele

Reputation: 33726

You can use the function Object.keys to loop the keys of the objects and with recursion, you can go deeper through the nested objects.

let obj = {'a': {'b': {'c': 12,'d': 'Hello World'},'e': [1,2,3]}},
    result = Object.create(null),
    loop = function (o, arr, result) {
      Object.keys(o).forEach(k => {
        arr.push(k);
        if (typeof o[k] === 'object' && !Array.isArray(o[k])) loop(o[k], arr, result);
        else result[arr.join('/')] = o[k];        
        arr.splice(-1);
      });
    };    
    
loop(obj, [], result);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 0

Related Questions