1

I have a problem with proper grouping of objects in an array.

I have an array in this format:

const places = [
  {
    id: 1,
    name: "p 1",
    icon: "icon-p",
    parent_id: null,
  },
  {
    id: 2,
    name: "p 2",
    icon: "icon-p",
    parent_id: 1,
  },
  {
    id: 3,
    name: "p 3",
    icon: "icon-p",
    parent_id: 1,
  },
  {
    id: 4,
    name: "t 1",
    icon: "icon-t",
    parent_id: null,
  },
  {
    id: 5,
    name: "t 2",
    icon: "icon-t",
    parent_id: 4,
  },
  {
    id: 6,
    name: "t 3",
    icon: "icon-t",
    parent_id: 4,
  },
  {
    id: 7,
    name: "a 1",
    icon: "icon-a",
    parent_id: null,
  },
  {
    id: 8,
    name: "b 1",
    icon: "icon-b",
    parent_id: null,
  },
  {
    id: 9,
    name: "h 1",
    icon: "icon-h",
    parent_id: null,
  },
  {
    id: 11,
    name: "h 2",
    icon: "icon-h",
    parent_id: 9,
  },
  {
    id: 12,
    name: "h 3",
    icon: "icon-h",
    parent_id: 9,
  },
  {
    id: 13,
    name: "h 4",
    icon: "icon-h",
    parent_id: 9,
  },
];


const groupReultsByIcon = places.reduce((r, { icon, ...object }) => {
  let temp = r.find((o) => o.icon === icon);
  if (!temp) r.push((temp = { icon, children: [] }));
  temp.children.push(object);
  return r;
}, []);


groupReultsByIcon.forEach(({ icon, children }) => {
  console.log('icon-name: ', icon);
  console.log('children-array: ', children);
});

The code that I have managed to prepare so far is, unfortunately, only grouping by icon.

I am trying to get such an array:

const newArrayObject = [
  {
    id: 1,
    icon: "icon-p",
    children: [
      {
        id: 1,
        name: "p 1",
        icon: "icon-p",
        parent_id: null,
      },
      {
        id: 2,
        name: "p 2",
        icon: "icon-p",
        parent_id: 1,
      },
      {
        id: 3,
        name: "p 3",
        icon: "icon-p",
        parent_id: 1,
      },
    ],
  },
  {
    id: 4,
    icon: "icon-t",
    children: [
      {
        id: 4,
        name: "t 1",
        icon: "icon-t",
        parent_id: null,
      },
      {
        id: 5,
        name: "t 2",
        icon: "icon-t",
        parent_id: 4,
      },
      {
        id: 6,
        name: "t 3",
        icon: "icon-t",
        parent_id: 4,
      },
    ],
  },
  {
    id: 7,
    icon: "icon-a",
    children: [
      {
        id: 7,
        name: "a 1",
        icon: "icon-a",
        parent_id: null,
      },
    ],
  },
  {
    id: 8,
    icon: "icon-b",
    children: [
      {
        id: 8,
        name: "b 1",
        icon: "icon-b",
        parent_id: null,
      },
    ],
  },
  {
    id: 9,
    icon: "icon-h",
    children: [
      {
        id: 9,
        name: "h 1",
        icon: "icon-h",
        parent_id: null,
      },
      {
        id: 11,
        name: "h 2",
        icon: "icon-h",
        parent_id: 9,
      },
      {
        id: 12,
        name: "h 3",
        icon: "icon-h",
        parent_id: 9,
      },
      {
        id: 13,
        name: "h 4",
        icon: "icon-h",
        parent_id: 9,
      },
    ],
  },
];

As you can see grouping should follow parent_id and id An object that parent_id === null should have children and in it itself as well as objects with its id.

Grzegorz T.
  • 3,903
  • 2
  • 11
  • 24
  • Does this answer your question? [Convert array of objects with parent ids to a nested tree structure](https://stackoverflow.com/questions/59779629/convert-array-of-objects-with-parent-ids-to-a-nested-tree-structure) – pilchard Apr 30 '22 at 11:00
  • also: [Build tree array from flat array in javascript](https://stackoverflow.com/questions/18017869/build-tree-array-from-flat-array-in-javascript) and [Make tree from array And change representation of parent field to object instead of ID](https://stackoverflow.com/questions/64680579/make-tree-from-array-and-change-representation-of-parent-field-to-object-instead) – pilchard Apr 30 '22 at 11:00

3 Answers3

1

You can check for children in one level and then add to it. This will check only for one nested level. You can make it recursive if you want.

Try like below

const places = [ { id: 1, name: "p 1", icon: "icon-p", parent_id: null, }, { id: 2, name: "p 2", icon: "icon-p", parent_id: 1, }, { id: 3, name: "p 3", icon: "icon-p", parent_id: 1, }, { id: 4, name: "t 1", icon: "icon-t", parent_id: null, }, { id: 5, name: "t 2", icon: "icon-t", parent_id: 4, }, { id: 6, name: "t 3", icon: "icon-t", parent_id: 4, }, { id: 7, name: "a 1", icon: "icon-a", parent_id: null, }, { id: 8, name: "b 1", icon: "icon-b", parent_id: null, }, { id: 9, name: "h 1", icon: "icon-h", parent_id: null, }, { id: 11, name: "h 2", icon: "icon-h", parent_id: 9, }, { id: 12, name: "h 3", icon: "icon-h", parent_id: 9, }, { id: 13, name: "h 4", icon: "icon-h", parent_id: 9, }, ];

const output = places.reduce((prev, curr) => {
  if (curr.parent_id === null) {
    prev.push(curr);
  } else {
    const parent = prev.find(({ id }) => id === curr.parent_id);
    if (parent) {
      parent.children
        ? parent.children.push(curr)
        : (parent.children = [{ ...parent }, curr]);
    }
  }
  return prev;
}, []);

console.log(JSON.stringify(output, null, 2));
Amila Senadheera
  • 12,229
  • 15
  • 27
  • 43
1

Using Array.reduce(), it can be done as follows:

const places = [
  {
    id: 1,
    name: "p 1",
    icon: "icon-p",
    parent_id: null,
  },
  {
    id: 2,
    name: "p 2",
    icon: "icon-p",
    parent_id: 1,
  },
  {
    id: 3,
    name: "p 3",
    icon: "icon-p",
    parent_id: 1,
  },
  {
    id: 4,
    name: "t 1",
    icon: "icon-t",
    parent_id: null,
  },
  {
    id: 5,
    name: "t 2",
    icon: "icon-t",
    parent_id: 4,
  },
  {
    id: 6,
    name: "t 3",
    icon: "icon-t",
    parent_id: 4,
  },
  {
    id: 7,
    name: "a 1",
    icon: "icon-a",
    parent_id: null,
  },
  {
    id: 8,
    name: "b 1",
    icon: "icon-b",
    parent_id: null,
  },
  {
    id: 9,
    name: "h 1",
    icon: "icon-h",
    parent_id: null,
  },
  {
    id: 11,
    name: "h 2",
    icon: "icon-h",
    parent_id: 9,
  },
  {
    id: 12,
    name: "h 3",
    icon: "icon-h",
    parent_id: 9,
  },
  {
    id: 13,
    name: "h 4",
    icon: "icon-h",
    parent_id: 9,
  },
];

const result = places.reduce((prev, curr) =>  {
  const parent = prev.find(p => p.id === curr.parent_id);
  if (parent) {
    if (!parent.children) {      
      parent.children = [];
    }
    parent.children.push(curr);
  } else {
    prev.push(curr);
  }  
  return prev;
}, []);

console.log(result);
uminder
  • 23,831
  • 5
  • 37
  • 72
1

You can easily achieve the result using Map as:

const places = [
  {
    id: 1,
    name: 'p 1',
    icon: 'icon-p',
    parent_id: null,
  },
  {
    id: 2,
    name: 'p 2',
    icon: 'icon-p',
    parent_id: 1,
  },
  {
    id: 3,
    name: 'p 3',
    icon: 'icon-p',
    parent_id: 1,
  },
  {
    id: 4,
    name: 't 1',
    icon: 'icon-t',
    parent_id: null,
  },
  {
    id: 5,
    name: 't 2',
    icon: 'icon-t',
    parent_id: 4,
  },
  {
    id: 6,
    name: 't 3',
    icon: 'icon-t',
    parent_id: 4,
  },
  {
    id: 7,
    name: 'a 1',
    icon: 'icon-a',
    parent_id: null,
  },
  {
    id: 8,
    name: 'b 1',
    icon: 'icon-b',
    parent_id: null,
  },
  {
    id: 9,
    name: 'h 1',
    icon: 'icon-h',
    parent_id: null,
  },
  {
    id: 11,
    name: 'h 2',
    icon: 'icon-h',
    parent_id: 9,
  },
  {
    id: 12,
    name: 'h 3',
    icon: 'icon-h',
    parent_id: 9,
  },
  {
    id: 13,
    name: 'h 4',
    icon: 'icon-h',
    parent_id: 9,
  },
];

const mapObj = places.reduce((acc, curr) => {
  const { id, name, icon, parent_id } = curr;
  if (!acc.get(parent_id)) {
    acc.set(id, {
      id,
      icon,
      children: [curr],
    });
  } else acc.get(parent_id)?.children?.push(curr);
  return acc;
}, new Map());

const result = [...mapObj.values()];
console.log(result);
DecPK
  • 24,537
  • 6
  • 26
  • 42