I have an array of date objects, which I want to group by year and month. The data structure I have chosen for my target is an array of arrays of Date objects.
Two Date objects should be in the same array if year AND month match up.
I'm supplying two extensions I added to Date and Formatter to easily extract year and month of a given date, as well as creating date objects more easily.
import Foundation
extension Formatter {
static let year: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy"
return formatter
}()
static let monthString: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "LLL"
return formatter
}()
static let monthInt: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "LL"
return formatter
}()
}
extension Date {
var monthString: String { return Formatter.monthString.string(from: self) }
var monthInt: Int { return Int(Formatter.monthInt.string (from:self))! }
var yearInt: Int { return Int(Formatter.year.string(from: self))!}
static func from(year: Int, month: Int, day: Int) -> Date {
let gregorianCalendar = NSCalendar(calendarIdentifier: .gregorian)!
var dateComponents = DateComponents()
dateComponents.year = year
dateComponents.month = month
dateComponents.day = day
let date:Date = gregorianCalendar.date(from: dateComponents)!
return date
}
static func parse(_ string: String, format: String = "yyyy-MM-dd") -> Date {
let dateFormatter = DateFormatter()
dateFormatter.timeZone = NSTimeZone.default
dateFormatter.dateFormat = format
let date = dateFormatter.date(from: string)!
return date
}
}
I then created a helper class with a static method to encapsulate the functionality:
class DateArrayConversionHelper {
static func sortDateByMonth(dateArray:[Date]) -> [[Date]] {
//create a copy of input array and sort it
var inputArray:[Date] = dateArray
inputArray.sort()
//create target data structure
var resultArray:[[Date]] = [[]]
//set initial variable and add it to target data structure
resultArray[0].append(inputArray[0])
var k = 0
for i in 1 ..< (inputArray.count) {
if (inputArray[i].yearInt == inputArray[i-1].yearInt)
&& (inputArray[i].monthInt == inputArray[i-1].monthInt) {
resultArray[k].append(inputArray[i])
}
else {
k = k+1
resultArray.append([])
resultArray[k].append(inputArray[i])
}
}
return resultArray
}
}
For testing:
let randomDates:[Date] = [Date.parse("2014-05-20"), Date.parse("2012-07-21"), Date.parse("2012-07-01"), Date.parse("2017-01-24"), Date.parse("2017-01-11"), Date.parse("2017-01-14"), Date.parse("2000-01-02"), Date.parse("2000-05-20")]
let resultData:[[Date]] = DateArrayConversionHelper.sortDateByMonth(dateArray: randomDates)
I'm looking for ways to improve my algorithm. I'm also still pretty new to Swift so if you're wondering why I didn't do/use x, it's usually because I didn't know better.
My end game is to use these arrays as a source for a UITableView
.
let date:Date = gregorianCalendar.date(from: dateComponents)!; return date
We can clearly see aDate
is being returned, and the namedate
doesn't provide any information. It's meaningless noise. Justreturn gregorianCalendar.date(from: dateComponents)!
\$\endgroup\$