I have a nested array that represents a parent child relationship, that I need to convert to a tree structure.
Example nested array
list = [
['lvl-1 item-1', 'lvl-2 item-1'],
['lvl-1 item-1', 'lvl-2 item-1', 'lvl-3 item-1'],
['lvl-1 item-1', 'lvl-2 item-1', 'lvl-3 item-2'],
['lvl-1 item-2', 'lvl-2 item-1', 'lvl-3 item-1'],
['lvl-1 item-2', 'lvl-2 item-2', 'lvl-3 item-2', 'lvl-4 item-1'],
]
Each tree node has the following structure.
{
name: "item",
parent: "parent_name",
children: []
}
Each item in the nested array represents a level in the object.
Expected JSON
{
"name": "0",
"parent": null,
"children": [
{
"name": "lvl-1 item-1",
"parent": "0",
"children": [
{
"name": "lvl-2 item-1",
"parent": "lvl-1 item-1",
"children": [
{
"name": "lvl-3 item-1",
"parent": "lvl-2 item-1",
"children": []
},
{
"name": "lvl-3 item-2",
"parent": "lvl-2 item-1",
"children": []
}
]
}
]
},
{
"name": "lvl-1 item-2",
"parent": "0",
"children": [
{
"name": "lvl-2 item-1",
"parent": "lvl-1 item-2",
"children": [
{
"name": "lvl-3 item-1",
"parent": "lvl-2 item-1",
"children": []
}
]
},
{
"name": "lvl-2 item-2",
"parent": "lvl-1 item-2",
"children": [
{
"name": "lvl-3 item-2",
"parent": "lvl-2 item-2",
"children": [
{
"name": "lvl-4 item-1",
"parent": "lvl-3 item-2",
"children": []
}
]
}
]
}
]
}
]
}
The code below is what I came up with, but there is a lot of room for improvement.
First I loop each item in the list and all of its children.
I add each level name to a parent array so I can navigate it later to find existing child objects. Once I hit the end of the child loop I add the current level to the parent for use on the next iteration.
Once I've reached the end of the parents for loop I check if the current name exists in the children and add it if not.
This was produced with a lot of trial and error, I'm struggling to explain it accurately.
Can someone help me to review this code, to better understand it and improve it.
I had trouble getting the initial structure to work, I ended up creating what I believe is a redundant object with a children
field so that it matched the expected format.
Any help will be much appreciated. If there is anything I haven't explained clearly please let me know.
list = [
['lvl-1 item-1', 'lvl-2 item-1'],
['lvl-1 item-1', 'lvl-2 item-1', 'lvl-3 item-1'],
['lvl-1 item-1', 'lvl-2 item-1', 'lvl-3 item-2'],
['lvl-1 item-2', 'lvl-2 item-1', 'lvl-3 item-1'],
['lvl-1 item-2', 'lvl-2 item-2', 'lvl-3 item-2', 'lvl-4 item-1'],
];
console.log(nestedArrayToJson(list));
function nestedArrayToJson(structure) {
const top_item = '0';
let data = {
children: [
{
name: top_item,
parent: null,
children: [],
}],
};
for(let i = 0; i < structure.length; i++) {
let parents = [top_item];
for(let j = 0; j < structure[i].length; j++) {
let obj = data;
for(parent of parents) {
obj = obj.children.find(o => o.name === parent);
}
const name = structure[i][j];
if(!obj.children.find(o => o.name === name)) {
obj.children.push({
name,
parent,
children: [],
});
}
parents.push(structure[i][j]);
}
}
return data.children[0];
}
Updated Script
list = [
['lvl-1 item-1', 'lvl-2 item-1'],
['lvl-1 item-1', 'lvl-2 item-1', 'lvl-3 item-1'],
['lvl-1 item-1', 'lvl-2 item-1', 'lvl-3 item-2'],
['lvl-1 item-2', 'lvl-2 item-1', 'lvl-3 item-1'],
['lvl-1 item-2', 'lvl-2 item-2', 'lvl-3 item-2', 'lvl-4 item-1']
]
function createTree(arr, topItem = "Top") {
const node = (name, parent = null) => ({
name,
parent,
children: []
});
const addNode = (parent, child) => {
parent.children.push(child);
return child;
};
const findNamedNode = (name, parent) => {
for (const child of parent.children) {
if (child.name === name) {
return child
}
const found = findNamedNode(name, child);
if (found) {
return found
}
}
};
const top = node(topItem);
let current;
for (const children of arr) {
current = top;
for (const name of children) {
const found = findNamedNode(name, current);
current = found ? found : addNode(current, node(name, current.name));
}
}
return top;
}
console.log(createTree(list, 'lvl-0 item-1'))