earl-95
earl-95

Reputation: 173

How to use reduce to return a mapped object with arrays as values?

I have an array of distinct objects. Some have the same date value. I'd like to group those with the same date together and return a mapped object where each date is the key and its value is an array of the object(s) with that same date. Here's the array:

const arr = [
  {
    date: '2/2/20',
    name: 'joe',
    age: 22
  },
  {
    date: '3/3/20',
    name: 'john',
    age: 67
  },
  {
    date: '2/2/20',
    name: 'chuck',
    age: 34
  },
  {
    date: '3/3/20',
    name: 'bob',
    age: 28
  },
  {
    date: '4/4/20',
    name: 'bill',
    age: 51
  }
]

I know something like this can be done with map().filter(), but it's not what I'm looking for, and plus I'm trying to do this with reduce() to save on resources.

So far I have:

arr.reduce((acc, person) => {
  acc[person.date] = [ person ];
  return acc;
}, {});

This only returns the last match, not all the matches:

{
  '2/2/20': [ { date: '2/2/20', name: 'chuck', age: 34 } ],
  '3/3/20': [ { date: '3/3/20', name: 'bob', age: 28 } ],
  '4/4/20': [ { date: '4/4/20', name: 'bill', age: 51 } ]
}

I've tried other aspects of this solution but I can't seem to get it right. I also know how to get the mapped object that returns the count of the number of matches, but it's still not my solution:

arr.reduce((acc, person) => {
  acc[person.date] = acc[person.date] + 1 || 1
  return acc
}, {})

This returns:

{ '2/2/20': 2, '3/3/20': 2, '4/4/20': 1 }

So how can I use reduce() to get an object that contains all the matches?

Upvotes: 0

Views: 62

Answers (2)

Damon
Damon

Reputation: 4336

Jack Bashford's answer appears to be what you are looking for. I would just add that this is a very common operation often referred to as groupBy. This is implemented in most functional helper libraries (Lodash for example). You can also implement it yourself with something like:

const groupBy = (cb, arr) =>
  arr.reduce((grouped, el) => {
    const key = cb(el)
    return {
      ...grouped,
      [key]: [...grouped[key] || [], el]
    }
  }, {})

(Note the above implementation is just an example and should not be considered a performant implementation. See Lodash groupBy implementation.

and then for your use case:

groupBy(el => el.date, arr)

which is helpful if you plan on doing similar operations.

Upvotes: 1

Jack Bashford
Jack Bashford

Reputation: 44107

Initialize to an empty array if acc[person.date] doesn't exist, and push regardless:

arr.reduce((acc, person) => {
    acc[person.date] = (acc[person.date] || []);
    acc[person.date].push(person);
    return acc;
}, {});

Destructuring one-line/spreading approach:

arr.reduce((acc, { date, ...o }) => ({ ...acc, [date]: [...(acc[date] || []), { date, ...o }), {});

Upvotes: 2

Related Questions