I have an array of ojects which all have a path and a name property. Like
[
{
"id": "1",
"path": "1",
"name": "root"
},
{
"id": "857",
"path": "1/857",
"name": "Animals"
},
{
"id": "1194",
"path": "1/857/1194",
"name": "Dinasours"
},
...and so on
]
Here are some path examples
1/1279/1282
1/1279/1281
1/1279/1280
1/857
1/857/1194
1/857/1194/1277
1/857/1194/1277/1278
I want to turn this into a multidimensional array like:
const data = {
id: "1",
name: "Root",
children: [
{
id: "1279",
name: "Toys",
},
{
id: "857",
name: "Animals",
children: [
{
id: "1194",
name: "Dinasours",
children: [
{
id: "1277",
name: "T-Rex",
children: [
{
id: "1278",
name: "Superbig T-Rex",
},
],
},
],
},
],
},
],
};
As you can understand the amount of data is much larger.
Is there a neat way to transform this data?
I wonder if this would be sufficient for your needs?
I'll refer to the objects as nodes (just because I'm a graph theory person, and that's how I roll).
Map
. (Purely for efficiency. You could technically find each node from scratch by id each time you need it.)This will result in nodes that have no children literally having no children
property (as opposed to having a children
property that is just []
). I also did not remove/delete the path
property from the objects.
As a note of caution, if there are path fragments that do not have corresponding objects, this will not work.
const nodes = [
{ id: '1', path: '1', name: 'root' },
{ id: '857', path: '1/857', name: 'Animals' },
{ id: '1194', path: '1/857/1194', name: 'Dinasours' }
//...and so on
];
const index = new Map();
for (let node of nodes) {
index.set(node.id, node)
}
for (let node of nodes) {
const fragments = node.path.split('/');
const parentId = fragments[fragments.length - 2];
const parent = index.get(parentId);
if (parent !== undefined) {
parent.children = parent.children || [];
if (!parent.children.includes(node)) {
parent.children.push(node);
}
}
}
// TODO: Decide which node is the root.
// Here's one way to get the first (possibly only) root.
const root = index.get(nodes[0].path.split('/')[0]);
console.dir(root, { depth: null });
Assuming that the root is always the same I came up with this code, it took me some time but it was fun to think about it.
var data = {};
list.forEach(item => {
var path = item.path.split("/");
let parent = data;
path.forEach((id) => {
if (!parent.id) {
parent.id = id;
parent.children = [];
if (id != item.id) {
let next = {}
parent.children.push(next);
parent = next;
}
} else if (parent.id != id) {
let next = parent.children.find(child => child.id == id);
if (!next) {
next = { id: id, children: [] }
parent.children.push(next);
}
parent = next;
}
});
parent.id = item.id;
parent.name = item.name
});
output:
{
"id": "1",
"children": [
{
"id": "857",
"children": [
{
"id": "1194",
"children": [
{
"id": "1277",
"children": [
{ "id": "1278", "children": [], "name": "Superbig T-Rex" }
],
"name": "T-Rex"
}
],
"name": "Dinasours"
}
],
"name": "Animals"
},
{ "id": "1279", "children": [], "name": "Toys" }
],
"name": "Root"
}
I think that having more roots here may need some fixing. Although I think the problem would be different if we were talking about multiple roots since your data variable is an object
Also, if you think in a recursive way it can be more understandable, but no comments on performance.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With