0

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:

  1. [duration: 35.5, endDate: date(), title :"reading"],
  2. [duration: 25.0, endDate: date(), title :"reading"],
  3. [duration: 20, endDate: date(), title :"memrise"],
  4. [duration: 70, endDate: date(), title :"memrise"],
  5. [duration: 50, endDate: (other date), title :"memrise"]

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)
}
2
  • Provide sample data please Commented Sep 5, 2018 at 17:32
  • added sample data - note that the endDate is just a Date variable. Commented Sep 5, 2018 at 17:44

1 Answer 1

2

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)
}
Sign up to request clarification or add additional context in comments.

1 Comment

Which would probably be a better format the resulting data anyway. But, if you really want two arrays that’s a pretty straightforward operation as well.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.