TRomesh TRomesh - 26 days ago 13
Javascript Question

How to loop through this nested object?

I have a dynamically generated JavaScript object which consist of nested objects and arrays. I couldn't find a proper way to list all nested objects, since that particular object is created dynamically.

Here is the object:

var tagdata = {
"Sentence": [{
"NP": [{
"NMP": "cat"
}, {
"NMP": "dog"
}]
}, {
"VP": [{
"KPD": "tree"
}, {
"NP": [{
"NMP": "ball"
}, {
"NMP": "bat"
}]
},{
"NP": [{
"NMP": "ground"
}, {
"NMP": "time"
}]
}]
}]
};


The output I require looks like this:

[{ key: 1, text: 'Sentence' },
{ key: 2, text: 'NP', parent: 1 },
{ key: 3, text: 'VP', parent: 1 },
{ key: 4, text: 'NMP', parent: 2 },
{ key: 5, text: 'NMP', parent: 2 },
{ key: 6, text: 'KPD', parent: 3 },
{ key: 7, text: 'NP', parent: 3 },
{ key: 8, text: 'NP', parent: 3 },
{ key: 9, text: 'cat', parent: 4 },
{ key: 10, text: 'dog', parent: 5 },
{ key: 11, text: 'tree', parent: 6 },
{ key: 12, text: 'NMP', parent: 7 },
{ key: 13, text: 'NMP', parent: 7 },
{ key: 14, text: 'NMP', parent: 8 },
{ key: 15, text: 'NMP', parent: 8 },
{ key: 16, text: 'ball', parent: 12},
{ key: 17, text: 'bat', parent: 13},
{ key: 18, text: 'ground', parent: 14},
{ key: 19, text: 'time', parent: 15},]


This data is to be used in a tree, so the order might be different, but the key:parent relationship should be maintained. Here is the code I've tried with:

let newtags=[{key:1,text:'Sentence'}];
tagdata["Sentence"].map( (elem,x) => {
newtags.push({key:x,text:Object.keys(elem)[0],parent:x});
if(Object.keys(elem)[0].length !== 0){
var y=x+1;
newtags.push({key:y,text:Object.values(elem)[0][x],parent:y});
}
});

console.log(newtags);

Answer

Your code works for the first level, but in your attempt to go one level deeper you will assign duplicate key values (y=x+1 when x will have that value in the next iteration of the .map() method as well).

What you need here is a recursive function, one that calls itself to deal with nested levels in the same way as any other level.

You could use this ES6, functional programming solution for that:

function listItems(obj) {
    var key = 0;
    return (function recurse(obj, parent = undefined) {
        return Object(obj) !== obj ? { key: ++key, text: obj, parent }
            :   Array.isArray(obj) ? Object.keys(obj).reduce( (acc, text) =>
                    acc.concat(recurse(obj[text], parent)), [])
            :   Object.keys(obj).reduce( (acc, text) =>
                    acc.concat({ key: ++key, text, parent },
                                recurse(obj[text], key)), []);
    })(obj);
}

// Sample data
var tagdata = {
  "Sentence": [{
      "NP": [{ "NMP": "cat" }, { "NMP": "dog" }]
    }, {
      "VP": [{
          "KPD": "tree"
        }, {
          "NP": [{ "NMP": "ball" }, { "NMP": "bat" }]
        },{
          "NP": [{ "NMP": "ground" }, { "NMP": "time" }]
        }]
    }]
};

// Extract the objects and output:
console.log(listItems(tagdata));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Comments