Rather than using dictionaries with a fixed set of keys, it's generally advisable to create your own custom types:
struct Person {
let lastName: String
let firstName: String
}
This way, you never have to worry about whether you got the key for a particular value in a dictionary right, because the compiler will enforce checks for the names of the property. It makes it easier to write robust, error-free code.
And, coincidentally, it makes sorting cleaner, too. To make this custom type sortable, you make it conform to the Comparable protocol:
extension Person: Comparable {
public static func ==(lhs: Person, rhs: Person) -> Bool {
return lhs.lastName == rhs.lastName && lhs.firstName == rhs.firstName
}
public static func < (lhs: Person, rhs: Person) -> Bool {
// if lastnames are the same, compare first names,
// otherwise we're comparing last names
if lhs.lastName == rhs.lastName {
return lhs.firstName < rhs.firstName
} else {
return lhs.lastName < rhs.lastName
}
}
}
Now, you can just sort them, keeping the comparison logic nicely encapsulated within the Person type:
let people = [Person(lastName: "Smith", firstName: "Robert"), Person(lastName: "Adams", firstName: "Bill")]
let sortedPeople = people.sorted()
Now, admittedly, the above dodges your implicit question of how to compare optionals. So, below is an example where firstName and lastName are optionals. But, rather than worrying about where to put the ? or !, I'd use nil-coalescing operator, ??, or a switch statement, e.g.:
struct Person {
let lastName: String?
let firstName: String?
}
extension Person: Comparable {
public static func ==(lhs: Person, rhs: Person) -> Bool {
return lhs.lastName == rhs.lastName && lhs.firstName == rhs.firstName
}
public static func < (lhs: Person, rhs: Person) -> Bool {
// if lastnames are the same, compare first names,
// otherwise we're comparing last names
var lhsString: String?
var rhsString: String?
if lhs.lastName == rhs.lastName {
lhsString = lhs.firstName
rhsString = rhs.firstName
} else {
lhsString = lhs.lastName
rhsString = rhs.lastName
}
// now compare two optional strings
return (lhsString ?? "") < (rhsString ?? "")
// or you could do
//
// switch (lhsString, rhsString) {
// case (nil, nil): return false
// case (nil, _): return true
// case (_, nil): return false
// default: return lhsString! < rhsString!
// }
}
}
The switch statement is more explicit regarding the handling of nil values (e.g. nil sorted before or after non-optional values) and will distinguish between a nil value and an empty string, should you need that. The nil coalescing operator is simpler (and IMHO, more intuitive for the end-user), but you can use switch approach if you want.
"last", e.g. checking such thatdicArray.contains(where: { !$0.keys.contains("last") })is nottrue. If you had incorporated such a guard in the first place, you would've caught the empty additional dictionary, as this would've violated the the input verification.