How do I group objects by key and modify the array the reduce function returns?

869 Views Asked by At

I use Redux to store all my products in a list. Displayed thist list looks like:

[
  {
    "id": 1,
    "name": "BLACK TEA",
    "supplieruuid": "SLIGRO",
  },
  {
    "id": 2,
    "name": "GREEN TEA",
    "supplieruuid": "SLIGRO",
  },
  {
    "id": 3,
    "name": "PURPLE TEA",
    "supplieruuid": "BUNZL",
  },
  {
    "id": 4,
    "name": "RAINBOW TEA",
    "supplieruuid": "BUNZL",
  },
] 

I'm using this reduce function to group these products together by key supplieruuid.

export const selectSortedItems = (state) => state.entities.cart.list.reduce((hash, { ["supplieruuid"]: value, ...rest }) => ({ ...hash, [value]: (hash[value] || []).concat({ ...rest }) }), {});

This returns this array of the products grouped by the key supplieruuid.

[
    {
        "SLIGRO": [
        {
            "id": 1,
            "name": "BLACK TEA",
            "supplieruuid": "SLIGRO",
        },
        {
            "id": 2,
            "name": "GREEN TEA",
            "supplieruuid": "SLIGRO",
        },
        ],
        "BUNZL": [
        {
            "id": 3,
            "name": "PURPLE TEA",
            "supplieruuid": "BUNZL",
        },
        {
            "id": 4,
            "name": "RAINBOW TEA",
            "supplieruuid": "BUNZL",
        },
        ],
    },
] 

except I need this returned as following:

 [
    {
        title: "SLIGRO",
        data: [
        {
            "id": 1,
            "name": "BLACK TEA",
        },
        {
            "id": 2,
            "name": "GREEN TEA",
        },
        ],
    },
    {
        title: "BUNZL",
        data: [
        {
            "id": 3,
            "name": "PURPLE TEA",
        },
        {
            "id": 4,
            "name": "RAINBOW TEA",
        },
        ],
    },
] 

How can I modify the reduce function to display the array as above? With the title and data added.

3

There are 3 best solutions below

0
Nina Scholz On BEST ANSWER

You could take a grouping with an object and a destructuring with unwanted properties for the data. At the end take only the values from the object.

const
    data = [{ id: 1, name: "BLACK TEA", supplieruuid: "SLIGRO" }, { id: 2, name: "GREEN TEA", supplieruuid: "SLIGRO" }, { id: 3, name: "PURPLE TEA", supplieruuid: "BUNZL" }, { id: 4, name: "RAINBOW TEA", supplieruuid: "BUNZL" }],
    result = Object.values(data.reduce((r, { supplieruuid: title, ...o }) => {
        (r[title] ??= { title, data: [] }).data.push(o);
        return r;
    }, {}));

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

3
Tomeu Cabot On

Keeping your selectSortedItems like it is, we could create another function to map it as you wish.

const selectSortedItems = (state) => {
  const sortedBySupplierUUID = state.entities.cart.list.reduce((hash, { ["supplieruuid"]: value, ...rest }) => ({ ...hash, [value]: (hash[value] || []).concat({ ...rest }) }), {});
  return Object.keys(selectSortedItems(sortedBySupplierUUID)).map(key => ({ title: key, data: sortedBySupplierUUID[key] }))};
}
0
DecPK On

As you are using reduce, and you need to pass an empty [] as an accumulator.

Second, you need to search for the element if it exists in acc array or not. If it exists then you push the object in the acc else add a new object with 2 property title and data.

const arr = [
  {
    id: 1,
    name: "BLACK TEA",
    supplieruuid: "SLIGRO",
  },
  {
    id: 2,
    name: "GREEN TEA",
    supplieruuid: "SLIGRO",
  },
  {
    id: 3,
    name: "PURPLE TEA",
    supplieruuid: "BUNZL",
  },
  {
    id: 4,
    name: "RAINBOW TEA",
    supplieruuid: "BUNZL",
  },
];

const result = arr.reduce((acc, curr) => {
  const { supplieruuid } = curr;
  const objInAcc = acc.find((o) => o.title === supplieruuid);
  if (objInAcc) objInAcc.data.push(curr);
  else acc.push({ title: supplieruuid, data: [curr] });
  return acc;
}, []);

console.log(result);