Home > Software design >  Reduce an array of objects by multiple keys and push them into a subset array
Reduce an array of objects by multiple keys and push them into a subset array

Time:01-24

I have the following object:

const array = [
    {city: 'Auckland', country: 'New Zealand', date: '2024-02-03T00:00:00'},
    {city: 'Manchester', country: 'United Kingdom', date: '2024-02-04T00:00:00'},
    {city: 'Manchester', country: 'United Kingdom', date: '2024-02-09T00:00:00'},
    {city: 'Edinburgh', country: 'Scotland', date: '2024-02-05T00:00:00'},
    {city: 'Manchester', country: 'United States', date: '2024-02-03T00:00:00'},
    {city: 'Manchester', country: 'United States', date: '2024-02-09T00:00:00'}
]

I want it grouped like the following:

{
    city: 'Auckland',
    items: [
        {city: 'Auckland', country: 'New Zealand', date: '2024-02-03T00:00:00'}
    ]
},
{
    city: 'Manchester',
    items: [
        {city: 'Manchester', country: 'United Kingdom', date: '2024-02-04T00:00:00'},
        {city: 'Manchester', country: 'United Kingdom', date: '2024-02-09T00:00:00'}
  ]
},
{
    city: 'Edinburgh',
    items: [
        {city: 'Edinburgh', country: 'Scotland', date: '2024-02-05T00:00:00'}
    ]
},
{
    city: 'Manchester',
    item: [
        {city: 'Manchester', country: 'United States', date: '2024-02-03T00:00:00'},
        {city: 'Manchester', country: 'United States', date: '2024-02-09T00:00:00'}
    ]
}

I have tried multiple different methods. I scoured the net for a similar thread, especially on here but have not found a similar one. I've tried the following:

array.reduce((r, item) => {
    r[item.city] = r[item.city] || []
    r[item.city].push(item)

    return r
}, Object.create(null))

However, this obviously does not take "country" into account. I've also tried the following but that only returns the first item in the desired group:

    const cities = {};

    for (let p of array) {
      const { city, country } = p;
      const groupByCity = JSON.stringify([city]);
      const groupByCountry = JSON.stringify([country]);
      if (!(groupByCity in cities)) {
        cities[groupByCity] = { city: city, items: []};
        cities[groupByCity].items.push(p);
      } else if (groupByCity in examCities && cities[groupByCity].country !== p.country) {
        cities[`${groupByCity}2`] = { city: city, items: []};
        cities[`${groupByCity}2`].items.push(p);
      }
    }

CodePudding user response:

There are many ways to do this, here's my way by using reduce and map:

const array = [{
    city: 'Auckland',
    country: 'New Zealand',
    date: '2024-02-03T00:00:00'
  },
  {
    city: 'Manchester',
    country: 'United Kingdom',
    date: '2024-02-04T00:00:00'
  },
  {
    city: 'Manchester',
    country: 'United Kingdom',
    date: '2024-02-09T00:00:00'
  },
  {
    city: 'Edinburgh',
    country: 'Scotland',
    date: '2024-02-05T00:00:00'
  },
  {
    city: 'Manchester',
    country: 'United States',
    date: '2024-02-03T00:00:00'
  },
  {
    city: 'Manchester',
    country: 'United States',
    date: '2024-02-09T00:00:00'
  }
]

const result = Object.entries(array.reduce((acc, curr) => {
  const key = `${curr.city}|${curr.country}`
  
  return {
    ...acc,
    [key]: key in acc ? [...acc[key], curr] : [curr]
  }
}, {})).map(([key, value]) => ({
  city: value[0].city,
  items: value
}))

console.log(result)

Basically I group them by {city}|{country} and form an object with reduce, then transform that object to the desired array with map.

  • Related