First I'd rewrite your struct a bit,
both the properties can be immutable.
Instead of a String for date, I use a NSDate, and a double for the duration
struct Hour {
let date: NSDate
let duration: Double
init(date: NSDate, duration: Double) {
self.date = date
self.duration = duration
}
}
I create the hours like
let hoursData = [
Hour(date: { let c = NSDateComponents(); c.day = 29; c.month = 11; c.year = 2015; return NSCalendar.currentCalendar().dateFromComponents(c)!}(), duration: 7),
Hour(date: { let c = NSDateComponents(); c.day = 12; c.month = 12; c.year = 2015; return NSCalendar.currentCalendar().dateFromComponents(c)!}(), duration: 7),
Hour(date: { let c = NSDateComponents(); c.day = 14; c.month = 12; c.year = 2015; return NSCalendar.currentCalendar().dateFromComponents(c)!}(), duration: 7),
Hour(date: { let c = NSDateComponents(); c.day = 15; c.month = 12; c.year = 2015; return NSCalendar.currentCalendar().dateFromComponents(c)!}(), duration: 7)]
if you wonder about this syntax: I use implicit unnamed closures to created the NSDate parameters
now I filter for month and year an use the reduce
method to sum up the filtered objects
let today = NSDate()
let totalDuration = hoursData.filter{
let objectsComps = NSCalendar.currentCalendar().components([.Month, .Year], fromDate: $0.date)
let todaysComps = NSCalendar.currentCalendar().components([.Month, .Year], fromDate: today)
return objectsComps.month == todaysComps.month && objectsComps.year == todaysComps.year
}.reduce(0) { (duration, hour) -> Double in
duration + hour.duration
}
Though it is a good answer, I want to point out that Rob's answer has a little flaw: The struct Hour
hides the dependency it has that the date string need to be of a certain format. This violates the Dependency Inversion Principle, the D in the SOLID Principles. But this is easy to fix.
Just pass in the date formatter when creating the Hour
instances.
struct Hour {
let date: NSDate
let duration: Double
init(date: NSDate, duration: Double) {
self.date = date
self.duration = duration
}
init (dateFormatter:NSDateFormatter, dateString:String, duration:Double) {
self.init(date: dateFormatter.dateFromString(dateString)!, duration:duration)
}
}
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "MMM d, y"
let hoursData = [
Hour(dateFormatter: dateFormatter, dateString: "Nov 29, 2015", duration: 7),
Hour(dateFormatter: dateFormatter, dateString: "Dec 12, 2015", duration: 7),
Hour(dateFormatter: dateFormatter, dateString: "Dec 14, 2015", duration: 7),
Hour(dateFormatter: dateFormatter, dateString: "Dec 25, 2015", duration: 7),
Hour(dateFormatter: dateFormatter, dateString: "Dec 25, 2017", duration: 7)
]
Now who ever uses Hour
can define the format as needed, might be helpful in localized apps.
The filtering and reducing stays the same.
But now we have a new issue: NSDateformatter's
dateFromString()
might return nil. currently we force unwrap it with !
, but this might be bad in a production app.
We should allow proper error handling, by allowing the convenience init to throw errors
enum HourError: ErrorType {
case InvalidDate
}
struct Hour {
let date: NSDate
let duration: Double
init(date: NSDate, duration: Double) {
self.date = date
self.duration = duration
}
init (dateFormatter:NSDateFormatter, dateString:String, duration:Double) throws {
let date = dateFormatter.dateFromString(dateString)
guard date != nil else { throw HourError.InvalidDate}
self.init(date: date!, duration:duration)
}
}
if we use it like
do {
let now = NSDate()
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "MMM d, y"
let hoursData = [
try Hour(dateFormatter: dateFormatter, dateString: "Nov 29, 2015", duration: 7),
try Hour(dateFormatter: dateFormatter, dateString: "Dec 12, 2015", duration: 7),
try Hour(dateFormatter: dateFormatter, dateString: "Dec 14, 2015", duration: 7),
try Hour(dateFormatter: dateFormatter, dateString: "Dec 25, 2015", duration: 7),
/*⛈*/ try Hour(dateFormatter: dateFormatter, dateString: " 25, 2017", duration: 7)
]
let totalDuration = hoursData.filter{
let objectsComps = NSCalendar.currentCalendar().components([.Month, .Year], fromDate: $0.date)
let todaysComps = NSCalendar.currentCalendar().components([.Month, .Year], fromDate: now)
return objectsComps.month == todaysComps.month && objectsComps.year == todaysComps.year
}.reduce(0) {
$0 + $1.duration
}
print(totalDuration)
} catch HourError.InvalidDate{
print("one or more datestrings must be wrong")
}
the error will be caught.
The full code