2

I have a dictionary in Swift that looks somewhat like this:

[
  "1": [
    "start": //An NSDate,
    "end": //An NSDate,
    "id": "1",
    "other non relevant items": "below",
  ]
]

And I want to sort it by the start date, with the key "start". I've seen the article on sorting dictionaries by value here but how would I do this with NSDates? Could you also give an example?

Community
  • 1
  • 1
jdejde
  • 45
  • 6
  • you can sort `NSArray` instances only, in an `NSDictionary` the order of the keys is always undetermined. do you have any `NSArray` in your hierarchy which you can sort? if not, create one first, and use one of the `–sortedArray...` method, or `NSMutableArray` and one of its `–sort...` methods. – holex Jul 06 '15 at 15:56
  • @holex Okay, I put the dictionaries in an array. What code would I use to get the new array sorted? – jdejde Jul 06 '15 at 15:59
  • convert your dates to proper `NSDate` instances, and insider the sorting method's block you can compare them; or even you can use predicates for that, but using a block for comparison looks much more simple in your case. – holex Jul 06 '15 at 16:01

1 Answers1

3

Note, you cannot sort a dictionary, except in the sense that you can sort any sequence, which turns it into an array of key/value pairs.

You haven’t said, but assuming your dictionary is of type [String:[String:AnyObject]], you could sort it (to get a result of an array of pairs, i.e. [(String,[String:AnyObject])]) like this:

let sorted = d.sort { lhs,rhs in
    let l = lhs.1["start"] as? NSDate
    let r = rhs.1["start"] as? NSDate
    return l < r
}

This is assuming you’ve also enhanced NSDate to have a < operator like so:

extension NSDate: Comparable { }

public func ==(lhs: NSDate, rhs: NSDate) -> Bool {
    return lhs.isEqualToDate(rhs)
}

public func <(lhs: NSDate, rhs: NSDate) -> Bool {
    return lhs.compare(rhs) == .OrderedAscending
}

Depending on what the actual type of your dictionary is, this might need some more type coercion.

If you don’t want the key in the sorted array (since it’s just a dupe of the ID), just sort the values:

let sorted = d.values.sort { lhs, rhs in
    let l = lhs["start"] as? NSDate
    let r = rhs["start"] as? NSDate
    return l < r
}

That said, I’d suggest instead of using a dictionary for anything more than the most basic of manipulation, that instead you write a custom struct and stash your data in that, this will make operations like this much simpler.

Airspeed Velocity
  • 40,491
  • 8
  • 113
  • 118
  • Could shorten by using $0 and $1 in the sorted. Nice answer though :) – boidkan Jul 06 '15 at 16:12
  • 1
    @boidkan my rule of thumb is I only tend to use $0, $1 in one-line closure expressions, or when the arguments have no meaningful names to give them (hence here, I prefer `lhs` and `rhs` since that’s the convention for binary predicates). – Airspeed Velocity Jul 06 '15 at 16:13
  • 1
    Sorry, that’s Swift 2 syntax, where every sequence has a sort method. For 1.2, you would need to use the free function i.e. `sort(d) { blah }` instead of `d.sort { blah }`. But in both cases, it works because dictionaries are sequences – a type that supports iteration of every element (though in the case of dictionary, in an undefined order) – Airspeed Velocity Jul 06 '15 at 16:28