Reputation: 271
I have the following class:
ChatMessage: Codable {
var id: Int?
var sender: User?
var message: String?
var seen: Int?
var tsp: Date?
}
The tsp is formatted like this: YYYY-MM-DD hh:mm:ss
I would like to "group" messages sent on the same day to end up with something like in this example:
let groupedMessages = [ [ChatMessage, ChatMessage], [ChatMessage, ChatMessage, ChatMessage] ]
I ultimately want to use groupedMessages in a UITableViewController to introduce sections like so:
func numberOfSections(in tableView: UITableView) -> Int {
return groupedMessages.count
// would be 2 int the above
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return chatMessages[section].count
// would be 2 for the first and 3 for the second section
}
What would be the most performant way of getting the sorting done? - i.e. something that also works well once the number of chatMessages to be sorted increases
Upvotes: 0
Views: 1047
Reputation: 705
let testArray = ["2016-06-23 09:07:21", "2016-06-23 08:07:21", "2016-06-22 09:07:21", "2016-06-22 08:07:21"]
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "YYYY-MM-DD hh:mm:ss"
let groupDic = Dictionary(grouping: testArray) { (pendingCamera) -> DateComponents in
let date = Calendar.current.dateComponents([.day, .year, .month], from: dateFormatter.date(from: pendingCamera)!)
return date
}
print(groupDic)
You can take groupDic and return in numberOfSections and titleForHeaderInSection.
Upvotes: 0
Reputation: 437
You could try:
let cal = Calendar.current
let groupedMessages = Dictionary(grouping: self.chatMessages, by: { cal.startOfDay($0.tsp) })
let keys = groupedMessages.keys.sorted(by: { $0 > $1 })
This however would give you a dictionary like:
[
SomeDateHere: [
ChatMessage(id: 0, sender: User?, message: "String", seen: 1),
ChatMessage(id: 0, sender: User?, message: "String", seen: 1)
],
AnotherDateHere: [
ChatMessage(id: 0, sender: User?, message: "String", seen: 1)
]
You could then use keys to return the section count:
return keys.count
And to get the array of messages for each dictionary item like so:
let key = keys[indexPath.section]
let messages = groupedMessages[key].sorted(by: { $0.tsp > $1.tsp })
Or to get just the count:
let key = keys[indexPath.section]
return groupedMessages[key].count
Upvotes: 2
Reputation: 130200
I would probably start by introducing a new type:
public struct CalendarDay: Hashable {
public let date: Date
public init(date: Date = Date()) {
self.date = Calendar.current.startOfDay(for: date)
}
}
extension CalendarDay: Comparable {
public static func < (lhs: CalendarDay, rhs: CalendarDay) -> Bool {
return lhs.date < rhs.date
}
}
Then I would extend your message:
extension ChatMessage {
var day: CalendarDay {
return CalendarDay(date: tsp)
}
}
(I am assumming tsp
is not an optional, there is no reason for it to be optional).
Now, let's define a section:
struct Section {
var messages: [ChatMessage] = []
let day: CalendarDay
init(day: CalendarDay) {
self.day = day
}
}
Let's group easily:
let messages: [ChatMessage] = ...
var sections: [CalendarDay: Section] = [:]
for message in messages {
let day = message.day
var section = sections[day] ?? Section(day: day)
section.messages.append(day)
sections[day] = section
}
let sectionList = Array(sections.values).sorted { $0.day < $1.day }
If your messages are not originally sorted, you might need to also sort messages in every section separately.
Upvotes: 0
Reputation: 434
Grouping array by date you could find here How to group array of objects by date in swift?
How to connect this to UITableView you will find here https://www.ralfebert.de/ios-examples/uikit/uitableviewcontroller/grouping-sections/
Upvotes: 0