Sort nil to the end of an array of optional strings

前端 未结 3 1766
不思量自难忘°
不思量自难忘° 2021-01-14 22:08

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         


        
相关标签:
3条回答
  • 2021-01-14 22:45

    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<T: Comparable>(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]
    
    0 讨论(0)
  • 2021-01-14 22:45

    Your example with Int gives a clue. If we had a max string value, we could plug that in.

    This works for strings that contain only alphabetic characters:

    let maxString = "~"
    ["b", nil, "a"].sorted{ $0 ?? maxString < $1 ?? maxString }
    

    Or simply:

    ["b", nil, "a"].sorted{ $0 ?? "~" < $1 ?? "~" }
    
    0 讨论(0)
  • 2021-01-14 22:49

    One nil is indistinguishable from another. So if you have a working solution that happens to sort as you desire except that nil entries wind up at the start, use it and then remove the nil entries and append the same number of nil entries to the end.

    Example:

        var arr : [String?] = [nil, "b", nil, "a", nil]
        arr = arr.sorted{ $0 ?? "" < $1 ?? "" }
        if let ix = arr.firstIndex(where: {$0 != nil}) {
            arr = arr.suffix(from: ix) + Array(repeating: nil, count: ix)
        }
        // [Optional("a"), Optional("b"), nil, nil, nil]
    
    0 讨论(0)
提交回复
热议问题