Reputation: 659
The function calculates the sum of duration values for each title. This works as written but I feel it could be improved upon with either calculating the sum of durations during the fetch or better use of .map and .reduce and somehow removing the for-loop.
The entity "record" has attributes "endDate", "duration" and "title" were titles map one to many.
Any insights would be appreciated.
Here is a sample data set for "record" entity:
for these, the predicate filters out the 5th entry and the function returns
(["reading","memrise"],[60.5, 90])
func getAllTitlesDurations(date: Date) -> (titles: [String], duration: [Double]) {
// date extension to get first and last moment of date
let dateDayStart = date.startOfDay
let dateDayNext = date.endOfDay
var durations : [Double] = []
var titles : [String] = []
// core data fetch, predicate to today
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return ([],[])
}
let context = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: entityNames.Record.rawValue)
fetchRequest.predicate = NSPredicate(format:"(endDate >= %@) AND (endDate < %@)", dateDayStart as CVarArg, dateDayNext as CVarArg)
do {
let records = try context.fetch(fetchRequest) as! [Record]
// create unique titles
titles = Array(Set(records.map{$0.title!}))
for m in titles {
durations.append(records
.filter{$0.title == m}
.reduce(0){$0 + $1.duration})
}
//MARK : Check outputs
print (durations)
print(titles)
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
return ([],[])
}
return (titles, durations)
}
Upvotes: 0
Views: 452
Reputation: 41226
You could do it all in the query using group by and a dictionary result. Simpler perhaps is to just collapse the n^^2 algorithm used to compute the duration into:
let result = records.reduce(into: [:]) { acc, record in
acc[record.title, default:0.0] += record.duration
}
That will build a dictionary of title to total duration in close to order n time.
If you really want two arrays, you can add:
.reduce(into: ([String](), [Double]())) {
$0.0.append($1.key)
$0.1.append($1.value)
}
Upvotes: 2