Monday, June 3, 2024
 Popular · Latest · Hot · Upcoming
111
rated 0 times [  118] [ 7]  / answers: 1 / hits: 32358  / 12 Years ago, thu, october 11, 2012, 12:00:00

My data has these properties:




  1. Each entry has a unique id (Id)

  2. Each has a Parent field, which points to the Id of the parent.

  3. A node can have multiple children, but only one parent.



My first attempt to build a tree is below. It is buggy as the recursion causes an infinite loop. Even if I solve it, I am not sure if there is a better approach to do this. Currently, I am doing it in 2 passes.



I would like it to be as efficient as possible as I have a decent amount of data. It also needs to rebuild the tree dynamically (the root can be any node)



There is sample data in the program below:



 arry = [{Id:1, Name:abc, Parent:}, {Id:2, Name:abc, Parent:1},
{Id:3, Name:abc, Parent:2},{Id:4, Name:abc, Parent:2}]//for testing


I was hoping the output to be (it might be wrong nested structure, as I manually wrote it. but, what I am hoping is a valid JSON structure with node as a field 'value' and children as an array.)



{
value: {Id:1, Name:abc, Parent:},
children: [
{
value: {Id:2, Name:abc, Parent:1},
children: [
{
value: {Id:3, Name:abc, Parent:2},
children: []
},
{
value: {Id:4, Name:abc, Parent:2},
children: []
}
]
..
}


Sample program:



function convertToHierarchy(arry, root) 
{
//root can be treated a special case, as the id is known
arry = [{Id:1, Name:abc, Parent:}, {Id:2, Name:abc, Parent:1},
{Id:3, Name:abc, Parent:2},{Id:4, Name:abc, Parent:2}]//for testing


var mapping = {}; // parent : [children]
for (var i = 0; i < array.length; i++)
{
var node = arry[i];

if (!mapping[node.Id]) {
mapping[node.Id] = {value: node, children:[] } ;
}else{
mapping[node.Id] = {value: node} //children is already set
}

if (!mapping[node.Parent]) { //TODO what if parent doesn't exist.
mapping[node.Parent] = {value: undefined, children:[ {value: node,children:[]} ]};
}else {//parent is already in the list
mapping[node.Parent].children.push({value: node,children:[]} )
}

}
//by now we will have an index with all nodes and their children.

//Now, recursively add children for root element.

var root = mapping[1] //hardcoded for testing, but a function argument
recurse(root, root, mapping)
console.log(root)

//json dump
}

function recurse(root, node, mapping)
{
var nodeChildren = mapping[node.value.Id].children;
root.children.push({value:node.value, children:nodeChildren})
for (var i = 0; i < nodeChildren.length; i++) {
recurse(root, nodeChildren[i], mapping);
}
return root;
}


I have 3 good solutions so far, and hope the upvotes suggest more idiomatic, efficient implementation. I am not sure, utilizing the property of my data that, there will be only one root element in the set of input array, and also the root is always given, any of these implementation could be better. I should also be learning how to benchmark, as my requirement is how efficiently (fast/without much memory) the tree can be rebuild. For example, the input is already cached (array) and rebuild the tree like



convertToHierarchy(parentid)
....
convertToHierarchy(parentid2)
...

More From » javascript

 Answers
49

Here's one solution:



var items = [
{Id: 1, Name: abc, Parent: 2},
{Id: 2, Name: abc, Parent: },
{Id: 3, Name: abc, Parent: 5},
{Id: 4, Name: abc, Parent: 2},
{Id: 5, Name: abc, Parent: },
{Id: 6, Name: abc, Parent: 2},
{Id: 7, Name: abc, Parent: 6},
{Id: 8, Name: abc, Parent: 6}
];

function buildHierarchy(arry) {

var roots = [], children = {};

// find the top level nodes and hash the children based on parent
for (var i = 0, len = arry.length; i < len; ++i) {
var item = arry[i],
p = item.Parent,
target = !p ? roots : (children[p] || (children[p] = []));

target.push({ value: item });
}

// function to recursively build the tree
var findChildren = function(parent) {
if (children[parent.value.Id]) {
parent.children = children[parent.value.Id];
for (var i = 0, len = parent.children.length; i < len; ++i) {
findChildren(parent.children[i]);
}
}
};

// enumerate through to handle the case where there are multiple roots
for (var i = 0, len = roots.length; i < len; ++i) {
findChildren(roots[i]);
}

return roots;
}

console.log(buildHierarchy(items));​

[#82616] Wednesday, October 10, 2012, 12 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
kaleyv

Total Points: 259
Total Questions: 99
Total Answers: 107

Location: Saint Helena
Member since Tue, Nov 3, 2020
4 Years ago
;