Can anyone help converting the following list of parent-child objects:
[ { \"name\":\"root\", \"_id\":\"root_id\", }, { \"name\":\"a1\"
Give it a try:
var obj = {};
obj.rootElements = [];
var currentRoot;
var currentParent;
for (s in a) {
var t = a[s];
var id = t._id;
if (t.parentAreaRef) {
var parentId = t.parentAreaRef.id;
if (parentId == currentParent._id) {
//add children
if (!currentParent.children) {
currentParent.children = [];
}
currentParent.children.push(t);
}
else {
addChildToParent(t, parentId);
}
}
else // is root
{
currentRoot = t;
currentParent = t;
obj.rootElements.push(currentRoot);
}
}
var t = currentRoot
function addChildToParent(child, parentId, root) {
for (p in a) {
if (a[p]._id.toString() == parentId.toString()) {
if (!a[p].children) {
a[p].children = [];
}
a[p].children.push(t);
}
}
}
There is an error in your string
a[p].children.push(t);
It should be
a[p].children.push(child);
also I'm little optimize it:
var data = [{"id":1,"name":"X","parentId":null},{"id":2,"name":"Y","parentId":1},{"id":3,"name":"D","parentId":2},{"id":2,"name":"S","parentId":1},{"id":5,"name":"K","parentId":4}]
var obj = {};
obj.rootElements = [];
for (i in data) {
var _elem = data[i];
if (_elem.parentId) {
var _parentId = _elem.parentId;
if (_parentId == _elem.id) {
// check children, if false - add
if (!_elem.children) {
_elem.children = [];
}
_elem.children.push(_elem);
}
else {
addChildToParent(_elem, _parentId);
}
}
else // is root
{
obj.rootElements.push(_elem);
}
}
function addChildToParent(child, parentId, root) {
for (j in data) {
if (data[j].id.toString() == parentId.toString()) {
if (!data[j].children) {
data[j].children = [];
}
data[j].children.push(child);
}
}
}
res.send(obj.rootElements);
You can use array-to-tree module from npm.
I know it's late, but I just finished this algorithm and maybe it can help some other people looking to solve the same problem: http://jsfiddle.net/akerbeltz/9dQcn/
A good thing about it is that it doesn't requires any special sort on the original object.
If you need to adapt it to your needs change the following lines:
Change the _id and the parentAreaRef.id depending on your structure.
if (String(tree[i]._id) === String(item.parentAreaRef.id)) {
Change the parentAreaRef depending on your structure.
if (tree[idx].parentAreaRef) buildTree(tree, tree.splice(idx, 1)[0])
Hope it helps!
UPDATE
Adding code here based on @Gerfried comment:
var buildTree = function(tree, item) {
if (item) { // if item then have parent
for (var i=0; i<tree.length; i++) { // parses the entire tree in order to find the parent
if (String(tree[i]._id) === String(item.parentAreaRef.id)) { // bingo!
tree[i].childs.push(item); // add the child to his parent
break;
}
else buildTree(tree[i].childs, item); // if item doesn't match but tree have childs then parses childs again to find item parent
}
}
else { // if no item then is a root item, multiple root items are supported
var idx = 0;
while (idx < tree.length)
if (tree[idx].parentAreaRef) buildTree(tree, tree.splice(idx, 1)[0]) // if have parent then remove it from the array to relocate it to the right place
else idx++; // if doesn't have parent then is root and move it to the next object
}
}
for (var i=0; i<data.length; i++) { // add childs to every item
data[i].childs = [];
}
buildTree(data);
console.log(data);
Thanks!
I know I'm too late, but since I just finished my contribution to a sample implementation of how this can be done I thought I would share it, since it might be found useful / or give inspiration to an alternative solution.
The implementation can be found here: http://jsfiddle.net/sw_lasse/9wpHa/
The main idea of the implementation centers around the following recursive function:
// Get parent of node (recursive)
var getParent = function (rootNode, rootId) {
if (rootNode._id === rootId)
return rootNode;
for (var i = 0; i < rootNode.children.length; i++) {
var child = rootNode.children[i];
if (child._id === rootId)
return child;
if (child.children.length > 0)
var childResult = getParent(child, rootId);
if (childResult != null) return childResult;
}
return null;
};
... that is used to build the tree.
Borrowing the caching logic from Vivin Paliath's answer, I have created a reusable function to convert a list of data with child-parent relationships into a tree.
var data = [
{ "id" : "root" },
{ "id" : "a1", "parentId" : "root", },
{ "id" : "a2", "parentId" : "a1", },
{ "id" : "a3", "parentId" : "a2", },
{ "id" : "b1", "parentId" : "root", },
{ "id" : "b2", "parentId" : "b1", },
{ "id" : "b3", "parentId" : "b1", }
];
var options = {
childKey : 'id',
parentKey : 'parentId'
};
var tree = walkTree(listToTree(data, options), pruneChildren);
document.body.innerHTML = '<pre>' + JSON.stringify(tree, null, 4) + '</pre>';
function listToTree(list, options) {
options = options || {};
var childKey = options.childKey || 'child';
var parentKey = options.parentKey || 'parent';
var childrenKey = options.childrenKey || 'children';
var nodeFn = options.nodeFn || function(node, name, children) {
return { name : name, children : children };
};
var nodeCache = {};
return list.reduce(function(tree, node) {
node[childrenKey] = [];
nodeCache[node[childKey]] = node;
if (typeof node[parentKey] === 'undefined' || node[parentKey] === '') {
tree = nodeFn(node, node[childKey], node[childrenKey]);
} else {
parentNode = nodeCache[node[parentKey]];
parentNode[childrenKey].push(nodeFn(node, node[childKey], node[childrenKey]));
}
return tree;
}, {});
}
function walkTree(tree, visitorFn, parent) {
if (visitorFn == null || typeof visitorFn !== 'function') {
return tree;
}
visitorFn.call(tree, tree, parent);
if (tree.children && tree.children.length > 0) {
tree.children.forEach(function(child) {
walkTree(child, visitorFn, tree);
});
}
return tree;
}
function pruneChildren(node, parent) {
if (node.children.length < 1) {
delete node.children;
}
}