1

Is there an efficient solution to group an array of dates into objects and subarrays grouped by day?

Example:

groupDatesByDay([
    new Date('2020-01-01 00:00:00'),
    new Date('2020-01-01 00:00:01'),
    new Date('2020-01-02 00:00:00'),
    new Date('2020-01-02 00:00:01'),
    new Date('2020-01-03 00:00:00'),
    new Date('2020-01-03 00:00:01'),   
])

The expected return value is the following, where there's an Object for each day with a key day representing the start of the day and an array containing all dates that are on the respective day. I am using Date-Fns' startOfDay() method.

[
    {
        day: Date Wed Jan 01 2020 00:00:00 GMT+0100 (Central European Standard Time),
        times: [
            Date Wed Jan 01 2020 00:00:00 GMT+0100 (Central European Standard Time),
            Date Wed Jan 01 2020 00:00:01 GMT+0100 (Central European Standard Time),
        ]
    },
    {
        day: Date Thu Jan 02 2020 00:00:00 GMT+0100 (Central European Standard Time),
        times: [
            Date Wed Jan 02 2020 00:00:00 GMT+0100 (Central European Standard Time),
            Date Wed Jan 02 2020 00:00:01 GMT+0100 (Central European Standard Time),
        ]
    },
    {
        day: Date Fri Jan 03 2020 00:00:00 GMT+0100 (Central European Standard Time),
        times: [
            Date Wed Jan 03 2020 00:00:00 GMT+0100 (Central European Standard Time),
            Date Wed Jan 03 2020 00:00:01 GMT+0100 (Central European Standard Time),
        ]
    },
]

I wrote a function that works, but the time complexity is really bad. Can this be done (much) better?

  groupDatesByDay(dates) {
    let grouped = []

    //Push first date to grouped
    grouped.push({
      day: startOfDay(dates[0]),
      times: [dates[0]]
    })

    //Iterate dates
    dates.forEach((date, inx) => {
      if (inx == 0) {
        return
      }

      let found = false

      for (let i=0; i<grouped.length; i++) {
        //Check if grouped contains day of date already      
        if (grouped[i].day.getTime() == startOfDay(date).getTime()) {
          grouped[i].times.push(date)
          found = true
          break
        }
      }

      if (found) {
        return
      } else {
        grouped.push({
          day: startOfDay(date),
          times: [date]
        })
      }
    });
    return grouped
  }
j.Doe
  • 202
  • 4
  • 18
  • 1
    See https://stackoverflow.com/questions/31890076/array-of-js-dates-how-to-group-by-days for options. – PM 77-1 Nov 25 '20 at 20:35
  • 1
    I am not using underscore.js which is why the linked question is irrelevant to my actual problem. – j.Doe Nov 25 '20 at 20:40
  • @j.Doe You were asking for "*is there a way*". The answer to that is **Yes**, using the standard group-by technique with buckets. You can implement this yourself with a `Map` or object (it's just few lines), or you can use a utility function library – Bergi Nov 25 '20 at 21:15
  • The question that @PM77-1 linked even has a non-underscore answer. – Bergi Nov 25 '20 at 21:17
  • 2
    @Bergi If you say there is a way, why don't you write an answer instead of closing a perfectly relevant question **that has no answer in the question that you linked**? The OP asked if this was possible in JavaScript, not with a third party library. The non-underscore answer of the question that was linked is incredibly unclean as it generates strings of every date before grouping it. This was neither asked nor is necessary for a clean solution. – dmuensterer Nov 30 '20 at 14:19
  • @dmuensterer How is that unclean or unnecessary? The `Date` objects need to be converted to a primitive to be used as a key for grouping. – Bergi Nov 30 '20 at 15:31
  • @dmuensterer Why would I write an answer when someone else already has done it? OP did not even ask for code, they asked for an approach with a better time complexity. This approach is described in the canonical topics I linked (and probably some more). Surely the OP can adapt it to use Date-Fns for generating days from his particular data. – Bergi Nov 30 '20 at 15:34

0 Answers0