javascript how to groupby

Viewed 362

how to groupby and convert array like this :

[ { date: '14/4/2021', item1:'test1', item2:'test2'}, { date: '14/4/2021', item1:'test3', item2:'test4'}, { date: '15/4/2021', item1:'test1', item2:'test2'}, ]

into this (groupby date):

[ {date:'14/4/2021',items:[{date: '14/4/2021', item1:'test1', item2:'test2'}, {date: '14/4/2021', items:[{date: '14/4/2021', item1:'test3', item2:'test4'}], {date:'15/4/2021',items:[{date: '15/4/2021', item1:'test1', item2:'test2'}] ]

2 Answers

This might be what you're looking for. We can use array methods like map() and filter() to isolate the data we want per date and then consolidate that into a single items array of strings per associated date.

1. Items as a single array of values only per date

First, I built an array of all the dates, excluding any duplicates using the ES6 […new Set([])] method and then I used the map() method on that array of dates to convert each date string into an object with the date set as one property, and then mapped the items data from your original array back into a new array under the items array of each date object.

Check it out:

const ungrouped = [
    { date: '14/4/2021', item1: 'test1', item2: 'test2' },
    { date: '14/4/2021', item1: 'test3', item2: 'test4' },
    { date: '15/4/2021', item1: 'test1', item2: 'test2' }
];

const groupArrayByDate = (arr => [...new Set(arr.map(e => e.date))].map(date => ({ date: date, items: arr.filter(e => e.date === date).flatMap(e => Object.entries(e).filter(f => f[0] !== 'date')).map(e => e[1]) })));

const grouped = groupArrayByDate(ungrouped);

console.log(grouped);

This outputs:

[
  {
    date: "14/4/2021",
    items: ["test1", "test2", "test3", "test4"]
  },
  {
    date: "15/4/2021",
    items: ["test1", "test2"]
  }
]

2. Items as a single object per date

If you'd prefer to have all your items be listed together in one object with numbered key values starting at 1 (index + 1), use this solution:

const ungrouped = [
    { date: '14/4/2021', item1: 'test1', item2: 'test2' },
    { date: '14/4/2021', item1: 'test3', item2: 'test4' },
    { date: '15/4/2021', item1: 'test1', item2: 'test2' }
];

const groupArrayByDate = arr => [...new Set(arr.map(e => e.date))].map(date => ({ date: date, items: Object.fromEntries(arr.filter(e => e.date === date).flatMap(e => Object.entries(e).filter(f => f[0] !== 'date')).map((e,i) => [`item${i+1}`,e[1]])) }));

const grouped = groupArrayByDate(ungrouped);

console.log(grouped);

This outputs:

[
  {
    date: "14/4/2021",
    items: { item1: "test1", item2: "test2", item3: "test3", item4: "test4" }
  },
  {
    date: "15/4/2021",
    items: { item1: "test1", item2: "test2" }
  }
]

3. Items as multiple objects per date (original keys per date object)

And if you'd like to leave the different objects separate under the items array, this should work for you as well. It's essentially the same as my above answer but without the additional mapping at the end:

const ungrouped = [
    { date: '14/4/2021', item1: 'test1', item2: 'test2' },
    { date: '14/4/2021', item1: 'test3', item2: 'test4' },
    { date: '15/4/2021', item1: 'test1', item2: 'test2' }
];

const groupArrayByDate = arr => [...new Set(arr.map(e => e.date))].map(date => ({ date: date, items: arr.filter(e => e.date === date).map(e => Object.entries(e).filter(f => f[0] !== 'date')).map(f => Object.fromEntries(f)) }));

const grouped = groupArrayByDate(ungrouped);

console.log(grouped);

This outputs:

[
  {
    date: "14/4/2021",
    items: [
      { item1: "test1", item2: "test2" },
      { item1: "test3", item2: "test4" }
    ]
  },
  {
    date: "15/4/2021",
    items: [
      { item1: "test1", item2: "test2" }
    ]
  }
]

The previous answer is correct but it also uses many loops through the array. I think a simple generator is enough to provide a cleaner implementation and less loops:

let ungrouped = [
    { date: '14/4/2021', item1: 'test1', item2: 'test2' },
    { date: '14/4/2021', item1: 'test3', item2: 'test4' },
    { date: '15/4/2021', item1: 'test1', item2: 'test2' }
];

function *groupByDate(list) {
    let groups = new Map();
    for (let item of list) {
        let group = groups.get(item.date) ?? [];
        group.push(item);
        groups.set(item.date, group);
    }
    for (let [date, items] of groups) {
        yield { date, items };
    }
}

let grouped = Array.from(groupByDate(ungrouped));

console.log(grouped);

This outputs:

[
  {
    date: '14/4/2021',
    items: [
      { date: '14/4/2021', item1: 'test1', item2: 'test2' },
      { date: '14/4/2021', item1: 'test3', item2: 'test4' }
    ]
  },
  {
    date: '15/4/2021',
    items: [
      { date: '15/4/2021', item1: 'test1', item2: 'test2' }
    ]
  }
]
Related