colindunn
colindunn

Reputation: 3163

iOS Swift: Sort array into multidimensional array

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)

Upvotes: 0

Views: 1704

Answers (3)

Edwin Vermeer
Edwin Vermeer

Reputation: 13127

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 }

Upvotes: 1

findall
findall

Reputation: 2193

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))

Upvotes: 2

kellanburket
kellanburket

Reputation: 12853

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.

Upvotes: 2

Related Questions