If I have an array of optional strings, and I want to sort it in ascending order with nils at the beginning, I can do it easily in a single line:
[\"b\", nil
You can provide a custom comparator which considers nil
as larger than any non-nil value:
let array = ["b", nil, "a", nil]
let sortedArray = array.sorted { (lhs, rhs) -> Bool in
switch (lhs, rhs) {
case let(l?, r?): return l < r // Both lhs and rhs are not nil
case (nil, _): return false // Lhs is nil
case (_?, nil): return true // Lhs is not nil, rhs is nil
}
}
print(sortedArray) // [Optional("a"), Optional("b"), nil, nil]
This works with any array of optional comparable elements, and avoids the usage of “magical large” values. The comparator can be implemented as a generic function:
func compareOptionalsWithLargeNil(lhs: T?, rhs: T?) -> Bool {
switch (lhs, rhs) {
case let(l?, r?): return l < r // Both lhs and rhs are not nil
case (nil, _): return false // Lhs is nil
case (_?, nil): return true // Lhs is not nil, rhs is nil
}
}
print(["b", nil, "a", nil].sorted(by: compareOptionalsWithLargeNil))
// [Optional("a"), Optional("b"), nil, nil]
print([2, nil, 1].sorted(by: compareOptionalsWithLargeNil))
// [Optional(1), Optional(2), nil]
print([3.0, nil, 1.0].sorted(by: compareOptionalsWithLargeNil))
// [Optional(1.0), Optional(3.0), nil]
print([Date(), nil, .distantPast, nil, .distantFuture].sorted(by: compareOptionalsWithLargeNil))
// [Optional(0000-12-30 00:00:00 +0000), Optional(2018-11-22 13:56:03 +0000),
// Optional(4001-01-01 00:00:00 +0000), nil, nil]