I have the following data structure
[
{ id: 1, comment: "a", comment_id: null },
{ id: 2, comment: "b", comment_id: null }
{ id: 3, comment: "c", comment_id: null },
{ id: 4, comment: "d", comment_id: 1 },
{ id: 5, comment: "e", comment_id: 2 },
{ id: 6, comment: "f", comment_id: 3 },
{ id: 7, comment: "g", comment_id: 4 },
{ id: 8, comment: "h", comment_id: 7 },
{ id: 9, comment: "i", comment_id: 7 },
{ id: 10, comment: "i", comment_id: 9 },
]
I am trying to unflatten this array to put the comments within a children's array into their respective object so the data turns out like so:
[
{
"id":1,
"comment":"a",
"comment_id":null,
"children":[
{
"id":4,
"comment":"d",
"comment_id":1,
"children":[
{
"id":7,
"comment":"g",
"comment_id":4,
"children":[
{
"id":8,
"comment":"h",
"comment_id":7
},
{
"id":9,
"comment":"i",
"comment_id":7,
"children":[
{
"id":10,
"comment":"i",
"comment_id":9
}
]
}
]
}
]
}
]
},
{
"id":2,
"comment":"b",
"comment_id":null,
"children":[
{
"id":5,
"comment":"e",
"comment_id":2
}
]
},
{
"id":3,
"comment":"c",
"comment_id":null,
"children":[
{
"id":6,
"comment":"f",
"comment_id":3
}
]
}
]
I know how to figure out what goes where with the following code, but I can't quite figure out a clean way to put it all into place.
const commentGrouping = allComments.reduce((groups:any, item:any) => {
const group = (groups[item.comment_id || "root"] || []);
group.push(item);
groups[item.comment_id || "root"] = group;
return groups;
}, {});
Any help would be appreciated.
CodePudding user response:
Rather than reduce, you might want to use recursion, tree structures work well with recursion.
eg.
const data = [
{ id: 1, comment: "a", comment_id: null },
{ id: 2, comment: "b", comment_id: null },
{ id: 3, comment: "c", comment_id: null },
{ id: 4, comment: "d", comment_id: 1 },
{ id: 5, comment: "e", comment_id: 2 },
{ id: 6, comment: "f", comment_id: 3 },
{ id: 7, comment: "g", comment_id: 4 },
{ id: 8, comment: "h", comment_id: 7 },
{ id: 9, comment: "i", comment_id: 7 },
{ id: 10, comment: "i", comment_id: 9 }
];
function fillTree(parentId) {
const ret = data.filter(d => d.comment_id === parentId);
for (const s of ret) {
const sh = fillTree(s.id);
if (sh.length) s.children = sh;
}
return ret;
}
console.log(fillTree(null));
CodePudding user response:
You can do this with a simple look up reduce. No fancy recursion needed. Just one loop over the data.
const data = [
{ id: 1, comment: "a", comment_id: null },
{ id: 2, comment: "b", comment_id: null },
{ id: 3, comment: "c", comment_id: null },
{ id: 4, comment: "d", comment_id: 1 },
{ id: 5, comment: "e", comment_id: 2 },
{ id: 6, comment: "f", comment_id: 3 },
{ id: 7, comment: "g", comment_id: 4 },
{ id: 8, comment: "h", comment_id: 7 },
{ id: 9, comment: "i", comment_id: 7 },
{ id: 10, comment: "i", comment_id: 9 },
];
const tree = data.reduce((acc, item) => {
// Find the parent and add it as a children
acc[item.comment_id].children.push(item);
// add a children array to our item
item.children = [];
// add the child to the look up so we can find it easily
acc[item.id] = item;
return acc;
}, { null: { children: [] } }).null.children;
console.log(tree);
If the data is not organized that parents are before the children
const data = [
{ id: 1, comment: "a", comment_id: null },
{ id: 2, comment: "b", comment_id: null },
{ id: 3, comment: "c", comment_id: null },
{ id: 4, comment: "d", comment_id: 1 },
{ id: 5, comment: "e", comment_id: 2 },
{ id: 6, comment: "f", comment_id: 3 },
{ id: 7, comment: "g", comment_id: 4 },
{ id: 8, comment: "h", comment_id: 7 },
{ id: 9, comment: "i", comment_id: 7 },
{ id: 10, comment: "i", comment_id: 9 },
];
const dataOpposite = data.reverse();
const tree = dataOpposite.reduce((acc, item) => {
// does the parent exist? If no, create a placeholder
if (!acc[item.comment_id]) {
acc[item.comment_id] = { children: [] };
}
// Find the parent and add it as a children
acc[item.comment_id].children.push(item);
// add a children array to our item
// if we have a placeholder created, use that
item.children = acc[item.id]?.children || [];
// add the child to the look up so we can find it easily
acc[item.id] = item
return acc;
}, { null: { children: [] } }).null.children;
console.log(tree);