-3

I have an array of Swift dates:

[date1, date2, date3, ..., dateN]

I want to group these dates by month:

[
    [date1, date2],
    [date3],
    ...
    [dateN],
]

All days of a month should be inside the same month array.

How can I group an array of dates by month, either functionally or imperatively?

pkamb
  • 33,281
  • 23
  • 160
  • 191
netshark1000
  • 7,245
  • 9
  • 59
  • 116

1 Answers1

12

Swift 4

This is how I would have done it:

extension Date {
    
    var month: Int {
        return Calendar.current.component(.month, from: self)
    }
    
}

// some random arbitrary dates
let rawDates = [Date(), Date().addingTimeInterval(100000.0), Date().addingTimeInterval(100000000.0)]
// the desired format
var sortedDatesByMonth: [[Date]] = []

// a filter to filter months by a given integer, you could also pull rawDates out of the equation here, to make it pure functional
let filterDatesByMonth = { month in rawDates.filter { $0.month == month } }
// loop through the months in a calendar and for every month filter the dates and append them to the array
(1...12).forEach { sortedDatesByMonth.append(filterDatesByMonth($0)) }

Tested and working in a Xcode 9.2 playground.

Output

[[], [], [2018-03-21 12:29:10 +0000, 2018-03-22 16:15:50 +0000], [], [2021-05-21 22:15:50 +0000], [], [], [], [], [], [], []]

Usage for hypothetical AppointmentObject

extension Date {
    
    var month: Int {
        return Calendar.current.component(.month, from: self)
    }
    
}

// some random arbitrary dates
let appointments = [AppointmentObject(), AppointmentObject(), AppointmentObject()]
// the desired format
var sortedAppointmentsByFromMonth: [[AppointmentObject]] = []

// a filter to filter months by a given integer, you could also pull rawDates out of the equation here, to make it pure functional
let filterFromDatesByMonth = { month in appointments.filter { $0.from.month == month } }
// loop through the months in a calendar and for every month filter the dates and append them to the array
(1...12).forEach { sortedAppointmentsByFromMonth.append(filterFromDatesByMonth($0)) }

Alternative

Not a direct answer to your question, but maybe a viable solution to your problem too. Many people, righteously, pointed out the existence of the Dictionary class. Using the above mentioned Date extension, you could also do this:

Dictionary(grouping: rawDates) {$0.month}

Output

Your keys are now the month indicators (5 being may and 3 march)

[5: [2021-05-21 22:46:44 +0000], 3: [2018-03-21 13:00:04 +0000, 2018-03-22 16:46:44 +0000]]

Alex Waygood
  • 6,304
  • 3
  • 24
  • 46
Alex
  • 838
  • 1
  • 14
  • 16
  • 1
    Thank you! My problem is a bit more complicated. The date object (from) is part of an AppointmentObject. I need AppointmentObjects grouped by month. let filterAppointmentsByMonth = { appointment in appointments.filter { $0.from.month == appointment.from.month } } gives an error (ambiguous reference) – netshark1000 Mar 21 '18 at 12:48