Home > Back-end >  unflatten array of objects by key
unflatten array of objects by key

Time:12-17

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);

  • Related