0

I have an array of CKRecords. Each record has startTime and a Name, among other values. What I would like to do is sort the records first by unique startTime and then within each startTime sort by unique Name.

The end result would be an array that looks like this (I think): records = [Date: [Name: [CKRecord]]]

Here is what I have right now:

func buildIndex(records: [CKRecord]) -> [[CKRecord]] {
var dates = [NSDate]()
var result = [[CKRecord]]()

for record in records {
    var date = record.objectForKey("startTime") as! NSDate

    if !contains(dates, date) {
        dates.append(date)
    }
}

for date in dates {
    var recordForDate = [CKRecord]()

    for (index, exercise) in enumerate(exercises) {
        let created = exercise.objectForKey("startTime") as! NSDate

        if date == created {
            let record = exercises[index] as CKRecord
            recordForDate.append(record)
        }
    }
    result.append(recordForDate)
}

return result
}

let records = self.buildIndex(data)

3 Answers 3

2

Why not use sorted? Like this.

// A simplified version of your `CKRecord` just for demonstration
struct Record {
    let time: NSDate
    let name: String
}

let records = [
    Record(time: NSDate(timeIntervalSince1970: 1), name: "a"),
    Record(time: NSDate(timeIntervalSince1970: 2), name: "b"),
    Record(time: NSDate(timeIntervalSince1970: 1), name: "c"),
    Record(time: NSDate(timeIntervalSince1970: 3), name: "d"),
    Record(time: NSDate(timeIntervalSince1970: 3), name: "e"),
    Record(time: NSDate(timeIntervalSince1970: 2), name: "f"),
]

func buildIndex(records: [Record]) -> [[Record]] {
    var g = [NSDate: [Record]]()
    for e in records {
        if (g[e.time] == nil) {
            g[e.time] = []
        }
        g[e.time]!.append(e) // grouping by `time`
    }
    return sorted(g.keys) { (a: NSDate, b: NSDate) in
        a.compare(b) == .OrderedAscending // sorting the outer array by 'time'
    }
    // sorting the inner arrays by `name`
    .map { sorted(g[$0]!) { $0.name < $1.name } } 
}

println(buildIndex(records))
Sign up to request clarification or add additional context in comments.

6 Comments

Can you write g[e.time] = (g[e.time] ?? []) + [e] in a less terse way? Trying to make sense of that line.
It's just to append an element to an array in the g dictionary.
I'm getting an Expected type compiler error for the .map { sorted(g[$0]!) { $0.name < $1.name } } line. Any idea what that means?
@colindunnn I've just tested my code above again, but I couldn't see that error. Did you modify the code? If so, please show me the modified version. I might be able to help you.
@colindunnn Do you mean something like this? gist.github.com/hisui/f4aea3d1d6f5881eebd2 (In that case, however, the output of buildIndex will have to be a 3-dimensional array of CKRecord..)
|
2

First of all, you're not really trying to sort an array here, you're trying to order a dictionary, which isn't built to be iterated over sequentially. In fact even if you do sort the array first and then build the dictionary like this:

var sortedRecords = [NSDate: [String: CKRecord]]()
records.sort { return $0.date.timeIntervalSinceDate($1.date) < 0 }

for record in records {
    if sortedRecords[record.date] != nil {
        sortedRecords[record.date] = [String: CKRecord]()
    }

    sortedRecords[record.date]![record.name] = record
}

The order isn't guaranteed when you iterate over it in the future. That said, a dictionary is essentially a look up table, and elements can be accessed in O(log n) time. What you'll really want to do is either drop the dictionary is favor of an array of [CKRecord] and then sort like this:

records.sort { $0.date.timeIntervalSinceDate($1.date) == 0 ? $0.name < $1.name : $0.date.timeIntervalSinceDate($1.date) < 0 }

Or, depending on what your end goal is, iterate across a range of dates, plucking the entries from the dictionary as you go.

2 Comments

Thanks for that explanation. I'm trying to display the records in two places. First as a tableview (where I need all records) and second in a detail view (where I need only a subset of the records). My goal with grouping is to make it easier to access only the information I need. But perhaps there is a better way.
Use the data structure that fits the particular problem. For the tableView you'll want to keep the records in a sorted array. For the detail view construct a getter that converts the array to a dictionary and returns it, e.g.: lazy var recordsByDate: [NSDate: [String:CKRecord]] { ... Dictionary Buildng Method Goes Here ... }
1

You could execute the CloudKit query and make sure that you get the array returned in the correct sort order like this:

    query.sortDescriptors = [NSSortDescriptor(key: "startTime", ascending: true), NSSortDescriptor(key: "Name", ascending: true)]

And then if you go to the detail view, you could use the filter for getting the records for that day like this:

   var details = records.filter { (%0.objectForKey("startTime") As! NSDate) == selectedDate }

Comments

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.