I want to generate a Array Tree of Objects with different levels and positions. In my opinion the parentId can create the level as in "children". The position can sort the items.
Can somebody help me out how I can achieve this?
I receive the following API data:
[
{ label: "Level one 1", id: 1, parentId: null, position: 0},
{ label: "Level two 1-1", id: 4, parentId: 1, position: 0},
{ label: "Level three 1-1-1", id: 9, parentId: 4, position: 1},
]
Here is an example how I want the data in the end:
const dataSource = ref([
{
id: 1,
position: 0,
parentId: null,
label: 'Level one 1',
children: [
{
id: 4,
position: 0,
parentId: 1,
label: 'Level two 1-1',
children: [
{
id: 9,
parentId: 4,
position: 0,
label: 'Level three 1-1-1',
},
{
id: 10,
parentId: 4,
position: 1,
label: 'Level three 1-1-2',
},
],
},
],
},
{
id: 2,
position: 1,
parentId: null,
label: 'Level one 2',
children: [
{
id: 5,
position: 0,
parentId: 2,
label: 'Level two 2-1',
},
{
id: 6,
position: 1,
parentId: 2,
label: 'Level two 2-2',
},
],
},
{
id: 3,
position: 2,
parentId: null,
label: 'Level one 3',
children: [
{
id: 7,
position: 0,
parentId: 3,
label: 'Level two 3-1',
},
{
id: 8,
position: 1,
parentId: 3,
label: 'Level two 3-2',
},
],
},
])
for this you can combine map method to redefine item in your array and filter to get only child of a parent object
var data = [
{ label: "Level one 1", id: 1, parentId: null, position: 0},
{ label: "Level two 1-1", id: 4, parentId: 1, position: 0},
{ label: "Level three 1-1-1", id: 9, parentId: 4, position: 1},
];
var result = data.map(elem => {
elem.children = data.filter(item => item.parentId === elem.id);
return elem;
});
console.log(result);
You could take a ingle loop approach with an object as reference for each node.
const
getTree = (data, root) => {
const t = {};
data.forEach(o => ((t[o.parentId] ??= {}).children ??= []).push(Object.assign(t[o.id] ??= {}, o)));
return t[root].children;
},
data = [{ label: "Level one 1", id: 1, parentId: null, position: 0 }, { label: "Level two 1-1", id: 4, parentId: 1, position: 0 }, { label: "Level three 1-1-1", id: 9, parentId: 4, position: 1 }],
tree = getTree(data, null);
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }
This solution first create a copy of each object, and stores it in a Map instance. I manually add the key null with value { children: [] }, so we can handle/find the root object easily.
After creating the Map instance, I loop over each records. Fetch both the copy of the record itself and the parent. Then assign parent.children to an array if it's not present. Finally I assign the record as a child of parent based on its position.
function recordsToTree(records) {
const lookup = new Map(records.map(({ ...record }) => [record.id, record]));
lookup.set(null, { children: [] });
for (const { id, parentId, position } of records) {
const record = lookup.get(id);
const parent = lookup.get(parentId);
parent.children ||= [];
parent.children[position] = record;
}
return lookup.get(null).children;
}
console.log(
recordsToTree([
{ label: "Level three 1-1-2", id: 3, parentId: 4, position: 1 },
{ label: "Level one 1", id: 1, parentId: null, position: 0 },
{ label: "Level three 1-1-1", id: 9, parentId: 4, position: 0 },
{ label: "Level two 1-1", id: 4, parentId: 1, position: 0 },
])
);
If position does not reflect the index of a child element you could use parent.children.push(record) instead of parent.children[position] = record.