javascriptarraysobjectnest-nested-object

How to convert array of object to nested object and vise versa in js


Array of objects where the hierarchy objects is stored in hierarchy
property. nesting of objects are done based on this hierarchy

[  
  {
    "hierarchy" : ["obj1"],  
    "prop1":"value" 
  },
  {  
    "hierarchy" : ["obj1","obj2"],  
    "prop2":"value",  
    "prop3":"value"  
  },  
  {  
    "hierarchy" : ["obj1","obj3"],  
    "prop4":"value",  
    "prop5":"value"  
  },  
  {  
    "hierarchy" : ["obj1","obj3", "obj4"],  
    "prop6":"value",  
    "prop7":"value",  
    "arr"  :["val1", "val2"]  
  }
]  

Expected nested object, hierarchy key removed here

{  
  "obj1":{  
    "prop1":"value",  
    "obj2" : {  
      "prop2":"value",  
      "prop3":"value"  
    },  
    "obj3":{  
      "prop4":"value",  
      "prop5":"value",  
      "obj4" : {  
        "prop6":"value",  
        "prop7":"value",  
        "arr"  :["val1", "val2"]  
      }  
    }  
  }  
}  

Code I tried but at line 8 unable to get the hierarchy

var input = "nested array as above";  
var output = {};  
var globalTemp = output;  
for(var i = 0 ; i<input.length ; i++){  
  var tempObj = input[i];  
  for(var key in tempObj){  
    if(key == "hierarchy"){     
      globalTemp = globlalTemp[tempObj[key]] = {};  
    }  
  }  
}  
console.log(globalTemp);

Solution

  • With a newer version of javascript, you could use restparameters for the wanted value key/value pairs and build a nested structure by iterating the given hierarchy property by saving the last property for assigning the rest properties.

    The reclaimed part getFlat uses an array as stack without recursive calls to prevent a depth first search which tries to get the most depth nodes first.

    At start, the stack is an array with an array of the actual object and another object with an empty hierarchy property with an empty array, because actually no key of the object is known.

    Then a while loop checks if the stack has some items and if so, it takes the first item of the stack and takes a destructuring assignment for getting an object o for getting back all key/value pairs and another object temp with a single property hierarchy with an array of the path to the object o.

    The push flag is set to false, because only found properties should be pushed later to the result set.

    Now all properties of the object are checked and if

    then a new object is found to inspect. This object is pushed to the stack with the actual path to it.

    If not, then a value is found. This key/value pair is added to the temp object and the flag is set to true, for later pushing to the result set.

    Proceed with the keys of the object.

    Later check push and push temp object with hierarchy property and custom properties to the result set.

    function getFlat(object) {
        var stack = [[object, { hierarchy: [] }]],
            result = [],
            temp, o, push;
    
        while (stack.length) {
            [o, temp] = stack.shift();
            push = false;
            Object.keys(o).forEach(k => {
                if (o[k] && typeof o[k] === 'object' && !Array.isArray(o[k])) {
                    stack.push([o[k], { hierarchy: temp.hierarchy.concat(k) }]);
                } else {
                    temp[k] = o[k];
                    push = true;
                }
            });
            push && result.push(temp);
        }
        return result;
    }
    
    var data = [{ hierarchy: ["obj1"], prop1: "value" }, { hierarchy: ["obj1", "obj2"], prop2: "value", prop3: "value" }, { hierarchy: ["obj1", "obj3"], prop4: "value", prop5: "value" }, { hierarchy: ["obj1", "obj3", "obj4"], prop6: "value", prop7: "value", arr: ["val1", "val2"] }],
        object = data.reduce((r, { hierarchy, ...rest }) => {
            var last = hierarchy.pop();
            hierarchy.reduce((o, k) => o[k] = o[k] || {}, r)[last] = rest;
            return r;
        }, {}),
        reclaimedData = getFlat(object);
    
    console.log(object);
    console.log(reclaimedData);
    .as-console-wrapper { max-height: 100% !important; top: 0; }