Efficiently performing multiple nested grouping and mapping in Javascript I have an array of log objects with log_types and dates, like this:
[
  {
    log_type: 'eventOne',
    date: '2020-08-27T00:00:00+00:00'
  },
  {
    log_type: 'eventOne',
    date: '2020-08-27T00:00:00+00:00'
  },
  {
    log_type: 'eventTwo',
    date: '2020-08-27T00:00:00+00:00'
  },
  {
    log_type: 'eventTwo',
    date: '2020-08-27T00:00:00+00:00'
  },
  {
    log_type: 'eventTwo',
    date: '2020-08-27T00:00:00+00:00'
  },
  {
    log_type: 'eventOne',
    date: '2020-08-27T00:00:00+00:00'
  },
]

For charting purposes, I need group all these logs by their log_types. Then I need to take all the logs for each log type, and group them by date. These will be formatted where t is the date and y is the count of that date occurring. Like this:
{
  eventOne: [
    { x: 2020-08-21T04:00:00.000Z, y: 0 },
    { x: 2020-08-22T04:00:00.000Z, y: 6 },
    { x: 2020-08-23T04:00:00.000Z, y: 0 },
    { x: 2020-08-24T04:00:00.000Z, y: 16 },
    { x: 2020-08-25T04:00:00.000Z, y: 0 },
    { x: 2020-08-26T04:00:00.000Z, y: 0 },
    { x: 2020-08-27T04:00:00.000Z, y: 22 }
  ],
  eventTwo: [
    { x: 2020-08-21T04:00:00.000Z, y: 0 },
    { x: 2020-08-22T04:00:00.000Z, y: 0 },
    { x: 2020-08-23T04:00:00.000Z, y: 1 },
    { x: 2020-08-24T04:00:00.000Z, y: 0 },
    { x: 2020-08-25T04:00:00.000Z, y: 0 },
    { x: 2020-08-26T04:00:00.000Z, y: 0 },
    { x: 2020-08-27T04:00:00.000Z, y: 17 }
  ]
}

So in plain english the goal is to get the number of events that were logged for each day, for each log type.
I've accomplished this, but it is a series of mangled lodash functions to where any sense of readability or efficiency has gone out the window.
// First group by log_type  
const logTypes = _.groupBy(logs, 'log_type');

const mappedLogs = {};
// Iterate through each log type
_.forOwn(logTypes, (value, key) =&gt; {
  const logDates = _.chain(value)
     // For each log type, group by date
     .groupBy('date')
     // Then map each log to the correct format
     .map((logValue, logKey) =&gt; ({ t: logKey, y: logValue.length }))
     .value();
  // Once we get the final mapped date values, assign them to the final object
  mappedLogs[key] = logDates;
});

I'm really hoping to find a way to do this more efficiently, I'm having trouble determining the efficiency of this current algorithm but I know the initial _groupBy touches each log O(n), the second _forOwn plus _groupBy I believe is another O(n), then the final mapping to correct format would own another O(n).
This is an application where there could be many thousands of logs so that is not an ideal efficiency.
I don't see what lodash has to offer here.  With modern JavaScript it can be written quite tersely thus:
const events = data.reduce((events, { log_type, date }) =&gt; {
  const event = (events[log_type] || (events[log_type] = []));
  const match = event.find(({ x }) =&gt; x == date);
  match ? match.y++ : event.push({ x: date, y: 1 });
  return events;
}, { });

The first line inside the .reduce method creates the event array if it doesn't exist.
The second line finds the element with a matching date.
The third line, if a matching date was found, increments its y property.  Else, it adds a new element with the date.
For example:


const data = [
  {
    log_type: 'eventOne',
    date: '2020-08-27T20:00:00+00:00'
  },
  {
    log_type: 'eventOne',
    date: '2020-08-27T20:00:00+00:00'
  },
  {
    log_type: 'eventTwo',
    date: '2020-08-27T00:00:00+00:00'
  },
  {
    log_type: 'eventTwo',
    date: '2020-08-27T00:00:00+00:00'
  },
  {
    log_type: 'eventTwo',
    date: '2020-08-27T40:00:00+00:00'
  },
  {
    log_type: 'eventOne',
    date: '2020-08-27T00:00:00+00:00'
  },
];

const events = data.reduce((events, { log_type, date }) =&gt; {
  const event = (events[log_type] || (events[log_type] = []));
  const match = event.find(({ x }) =&gt; x == date);
  match ? match.y++ : event.push({ x: date, y: 1 });
  return events;
}, { });

console.log(events);