How to Group Array of Objects by Date in Swift

How to group array of objects by date in swift?

This is my solution to group by date(just day, month and year):

let groupDic = Dictionary(grouping: arr) { (pendingCamera) -> DateComponents in

let date = Calendar.current.dateComponents([.day, .year, .month], from: (pendingCamera.date)!)

return date
}

How to group Arrays by Date in Swift?

You can use Calendar startOfDay.

let cal = Calendar.current
let grouped = Dictionary(grouping: orders.results, by: { cal.startOfDay(for: $0.createdAt) })

How to group array of dates by months in swift?

Here's the code for a Playground

I think your structs should probably be Int values for the months, as when you go to populate something like a tableview, it'll be a PITA to re-order months if you've got them as Strings.

struct ScheduleDates {
var month: String
var dates: [Date]
}

Anyway, here's the extension I wrote based on what you provided. I frankly think you should return a dictionary with an Int as the key and an array of Dates as the value, but here's what you wanted...

I used Dictionary(grouping:by:) to construct a dictionary from an array of dates.

extension Date {
static func dateDictionary(from arrayOfDates: [Date]) -> [String: [Date]] {
// declare a dictionary
return Dictionary(grouping: arrayOfDates) { date -> String in
// get the month as an int
let monthAsInt = Calendar.current.dateComponents([.month], from: date).month
// convert the int to a string...i think you probably want to return an int value and do the month conversion in your tableview or collection view
let monthName = DateFormatter().monthSymbols[(monthAsInt ?? 0) - 1]
// return the month string
return monthName
}
}
}

Here's a utility method I wrote to generate data while I figured out how to do it. If you're going to be in production, don't force unwrap stuff as I did here.

// Utility method to generate dates
func createDate(month: Int, day: Int, year: Int) -> Date? {
var components = DateComponents()
components.month = month
components.day = day
components.year = year

return Calendar.current.date(from: components)!
}

Below is how I generated an array of sample dates to experiment.

// generate array of sample dates
let dateArray: [Date] = {
let months = Array(1...12)
let days = Array(1...31)
let years = [2019]

var dateArray: [Date] = []

while dateArray.count < 100 {
let randomMonth = months.randomElement()
let randomDay = days.randomElement()
let randomYear = years.randomElement()

if let month = randomMonth,
let day = randomDay,
let year = randomYear,
let date = createDate(month: month,
day: day,
year: year) {
dateArray.append(date)
}
}

return dateArray
}()

let monthDictionary = Date.dateDictionary(from: dateArray)

var arrayOfStructs: [ScheduleDates] = []

monthDictionary.keys.forEach { key in
let scheduleDate = ScheduleDates(month: key,
dates: monthDictionary[key] ?? [])
arrayOfStruct.append(scheduleDate)
}

print(arrayOfStructs)

How to group array data based on Date item into a dictionary

Given we fix your example up somewhat (using String as Date example), you could simply pass over the elements in data and add to (and optionally increase) the corresponding key-value pair in the dict.

struct Item {
let date: String
let amount: Double
}

let data = [Item(date: "2017.02.15", amount: 25),
Item(date: "2017.02.14", amount: 50),
Item(date: "2017.02.11", amount: 35),
Item(date: "2017.02.15", amount: 15)]

var dict: [String: Double] = [:]
for item in data {
dict[item.date] = (dict[item.date] ?? 0) + item.amount
}

print(dict) // ["2017.02.14": 50.0, "2017.02.15": 40.0, "2017.02.11": 35.0]

Group array of Swift dates by month

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



Related Topics



Leave a reply



Submit