1

I have structure database in array of objects stored like this:

 arry = [{"name": "a", "id": "2", "data":"foo", "parent": "1"},
 {"name": "b", "id": "3", "data":"foo", "parent": "2"},
 {"name": "c", "id": "4", "data":"foo", "parent": "3"},
 {"name": "d", "id": "5", "data":"foo", "parent": "3"},
 {"name": "e", "id": "6", "data":"foo", "parent": "4"},
 {"name": "f", "id": "7", "data":"foo", "parent": "5"}]

I want nested structure like this

{
"2":{
   "name": "a",
   "data": "foo",
  "3":{
     "name": "b",
     "data":"foo",
     "4":{
        "name": "c",
        "data":"foo",
        "6":{
           "name": "e",
           "data": "foo",
          };
       },
      "5":{
         "name": "d",
         "data": "foo",
         "7":{
            "name": "f",
            "data": "foo"
           }
        }
      }
    }
  };

so I can use it Angular Material tree.

Eka hersada
  • 15
  • 1
  • 6
  • This is more of a programming problem than an angular one. Also, what is your data source for the table, can you guarantee that there wont be any cycles? –  Oct 17 '18 at 05:43

1 Answers1

5

To do this, you can reduce your array of nodes to a dictionary, using each node's id as index.

This way, you'll have all the nodes accessible by their id directly on the dictionary. You'll thus be able to store each node in its parent easily.

Once all the nodes are stored in their respective parent, you just have to grab the root node from the dictionary, it will hold all your tree.

It may happen that the parent isn't yet in the dictionary when you parse the child, in this case, you can use a dummy object that will play a placeholder the time we come to parse the actual parent node.

var arry = [
 {"name": "a", "id": "2", "data":"foo", "parent": "1"},
 {"name": "b", "id": "3", "data":"foo", "parent": "2"},
 {"name": "c", "id": "4", "data":"foo", "parent": "3"},
 {"name": "d", "id": "5", "data":"foo", "parent": "3"},
 {"name": "e", "id": "6", "data":"foo", "parent": "4"},
 {"name": "f", "id": "7", "data":"foo", "parent": "5"}
];

function totree(branches, node) {
  // if we don't have the parent yet
  if (!branches[node.parent]) {
    // create a dummy placeholder for now
    branches[node.parent] = {};
  }
  // store our node in its parent
  branches[node.parent][node.id] = node;
  // store our node in the full list
  // copy all added branches on possible placeholder
  branches[node.id] = Object.assign(node, branches[node.id]);

  return branches;
}

var tree = arry.reduce(totree, {})['1']; // get only the root node ('1')

console.log(tree);
Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • Although this solution is elegant, it does not solve the generic case when a tree can have multiple root nodes. We'd still have to iterate through the dictionary to create the final structure. – AsGoodAsItGets Oct 07 '19 at 15:16
  • @AsGoodAsItGets althought there are graphs with no root node, a tree always has a single one. – Kaiido Oct 07 '19 at 22:49
  • @Kaiido I assume you're refering to the tree data structure. I'm refering to the generic use of trees as hierarchical UI elements, in which case of course you can have multiple roots. We are not talking about graph data structures. – AsGoodAsItGets Oct 08 '19 at 12:27
  • And to answer the original question, a complete and far more elegant ES6-only solution is this one: https://stackoverflow.com/a/55241491/134120 – AsGoodAsItGets Oct 08 '19 at 12:29
  • @AsGoodAsItGets hum... please re-read the question here. Yes we are exactly talking about Graph data structure here. And sorry but recursive approach building a lot of mess in the GC is not what I call an "elegant" solution, even though it's done writing a bit less characters (is it really btw?). – Kaiido Oct 08 '19 at 13:35
  • @Kaiido I re-read the question and I still stand by what I wrote. Although the example that the OP gave has only one root, a generic solution must take into account the existence of multiple roots. Especially in the context that the OP has described, i.e. building an Angular Material tree from database data returned in JSON, which is exactly what I'm currently doing with the above mentioned solution. If performance is an issue (as you imply by the recursive approach), then is should be done in the back-end anyway. – AsGoodAsItGets Oct 08 '19 at 14:45
  • @AsGoodAsItGets OP asked for a return structure with a single entry point. The title of the question asks for a "tree". Returning an Array would have made my answer fail to answer both OP's request and future readers expectation. If you want to handle such a particular case, then it's as easy as iterating over the generated map in search for orphans nodes. https://jsfiddle.net/9mb4urjs/ It's still far more efficient and elegant than the poor solution you linked to. But that's not an answer to this question. – Kaiido Oct 08 '19 at 15:04
  • @Kaiido well, let's leave the future readers of these comments to use whatever works for them. Have a nice day :) – AsGoodAsItGets Oct 08 '19 at 15:27