Search code examples
javascriptrecursiontreeadjacency-list

Tree Structure from Adjacency List


I am trying to generate a hierarchical tree object from a flat array with parent IDs.

// `parent` represents an ID and not the nesting level.
var flat = [
    { id: 1, name: "Business", parent: 0 },
    { id: 2, name: "Management", parent: 1 },
    { id: 3, name: "Leadership", parent: 2 },
    { id: 4, name: "Finance", parent: 1 },
    { id: 5, name: "Fiction", parent: 0 },
    { id: 6, name: "Accounting", parent: 1 },
    { id: 7, name: "Project Management", parent: 2  }
];

The final tree object should look as follows:

{ 
    id: 1, 
    name: "Business", 
    children: [
        { 
            id: 2, 
            name: "Management", 
            children: [
                { id: 3, name: "Leadership" },
                { id: 7, name: "Project Management" }
            ]
        }
        // [...]
    ]
}
// [...]

You can see my current work on this fiddle, but it only works on the first two levels.

I thought about collecting the orphans (objects from flat without a parent in tree yet) and then looping over them again to see if they now have a parent. But that could mean many loops over the tree object, especially with many categories over multiple levels.

I'm sure there is a more elegant solution.


Solution

  • Looks like you can do this in 2 passes no matter the tree depth:

    var flat = [
        { id: 1, name: "Business", parent: 0 },
        { id: 2, name: "Management", parent: 1 },
        { id: 3, name: "Leadership", parent: 2 },
        { id: 4, name: "Finance", parent: 1 },
        { id: 5, name: "Fiction", parent: 0 },
        { id: 6, name: "Accounting", parent: 1 },
        { id: 7, name: "Project Management", parent: 2  }
    ];
    
    var nodes = [];
    var toplevelNodes = [];
    var lookupList = {};
    
    for (var i = 0; i < flat.length; i++) {
        var n = {
            id: flat[i].id,
            name: flat[i].name,
            parent_id: ((flat[i].parent == 0) ? null : flat[i].parent),
            children: []
            };
        lookupList[n.id] = n;
        nodes.push(n);
        if (n.parent_id == null) {
            toplevelNodes.push(n);
        }
    }
    
    for (var i = 0; i < nodes.length; i++) {
      var n = nodes[i];
      if (!(n.parent_id == null)) {
          lookupList[n.parent_id].children = lookupList[n.parent_id].children.concat([n]);
      }
    }
    
    console.log(toplevelNodes);