Reputation: 5387
I have a JavaScript array like this:
[{
name:'a'
level:1
},{
name:'b'
level:2
},{
name:'c'
level:3
},{
name:'d'
level:2
},{
name:'e'
level:1
},{
name:'f'
level:2
},{
name:'g'
level:2
},{
name: 'f',
level: 1
}
]
I need this to be converted into a tree structure based on level
property of objects
[{
name: 'a'
level: 1
children: [{
name: 'b'
level: 2,
children: [{
name: 'c',
level: 3
}]
}, {
name: 'd'
level: 2
}]
}, {
name: 'e'
level: 1,
children: [{
name: 'f'
level: 2
}, {
name: 'g'
level: 2
}]
}, {
name: 'f',
level: 1
}]
I have tried writing many functions and tried many approaches but failed. One thing I realized is that this can be achieved only by writing a recursive function. Please help.
NOTE: the level depth is not limited to 3, it is unknown
Upvotes: 0
Views: 1623
Reputation: 512
I had this exact same problem, and this was my solution:
function constructTree (flatList) {
const tree = [];
const clonedList = cloneDeep(flatList);
let root = {};
function append (child, parent) {
if (Array.isArray(parent.children)) {
parent.children.push(child);
} else {
parent.children = [child];
}
return parent;
}
function appendParentAdjacentNode (level, node, nodes) {
if (level === 0) {
nodes.push(node);
return node;
}
while (level > 0) {
return appendParentAdjacentNode(level - 1, node, nodes[0].children);
}
return node;
}
let parent;
for (let i = 0; i < clonedList.length; i++) {
const currentNode = clonedList[i];
const previousNode = clonedList[i - 1];
const isRoot = currentNode.level === 0;
if (isRoot) {
root = currentNode;
tree.push(root);
continue;
}
const isChild = currentNode.level > previousNode.level;
const isSibling = currentNode.level === previousNode.level;
const isParentAdjacent = currentNode.level < previousNode.level;
if (isChild) {
parent = previousNode;
}
if (isChild || isSibling) {
append(currentNode, parent);
}
if (isParentAdjacent) {
appendParentAdjacentNode(currentNode.level - 1, currentNode, root.children);
}
}
return tree;
}
Brief explanation:
Upvotes: 0
Reputation: 26873
Here is a quick go at it:
var res = convert(a);
console.log(res);
function convert(arr) {
return treeify(splitToSegments(arr));
}
function splitToSegments(arr) {
var ret = [];
var accum, segment;
arr.forEach(function(o) {
if (o.level === 1) {
accum = true;
if (segment && segment.length) {
ret.push(segment);
}
segment = [o];
} else if (accum) {
segment.push(o);
}
});
if (segment && segment.length) {
ret.push(segment);
}
return ret;
}
function treeify(arr) {
return arr.map(function(o) {
return o.reduce(function(a, b) {
var lastChild;
a.children = a.children || [];
if (a.level + 1 === b.level) {
a.children.push(b);
} else {
lastChild = a.children[a.children.length - 1];
lastChild.children = lastChild.children || [];
lastChild.children.push(b);
}
return a;
});
});
}
Note that treeify
might need some extra logic to deal with the siblings but it should be an okish starting point.
Upvotes: 3