swiftnsdatenscalendar

Group array of Swift dates by month


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?


Solution

  • 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]]