施瓦茨变换 (MIT 许可证)

高级排序

什么是施瓦茨变换?

https://en.wikipedia.org/wiki/Schwartzian_transform

用例

let input = [
    Record(id: 5, text: "B"),
    Record(id: 0, text: "A"),
    Record(id: 7, text: nil),
    Record(id: 1, text: "a"),
    Record(id: 6, text: nil),
    Record(id: 3, text: "B"),
    Record(id: 8, text: nil),
    Record(id: 4, text: "b"),
    Record(id: 2, text: "A"),
]

// Sorting challenge:
// Lowercase compare `text` fields.
// When text is the same or nil, then fallback using the `id` field.

let output = [
    Record(id: 0, text: "A"),
    Record(id: 1, text: "a"),
    Record(id: 2, text: "A"),
    Record(id: 3, text: "B"),
    Record(id: 4, text: "b"),
    Record(id: 5, text: "B"),
    Record(id: 6, text: nil),
    Record(id: 7, text: nil),
    Record(id: 8, text: nil),
]

当处理大型类时,事情会变得混乱。这就是施瓦茨变换的用武之地。

然而,对于这个简单的 Record 类,那么可以使用 Apple 的 Collection.sorted 函数,像这样

let output = input.sorted {
    ($0.text?.lowercased() ?? "nil", $0.id) < ($1.text?.lowercased() ?? "nil", $1.id)
}

或者像这样

let output = input.sorted(by: { (lhs, rhs) -> Bool in
    if let text0 = lhs.text?.lowercased(), let text1 = rhs.text?.lowercased() {
        if text0 == text1 {
            return lhs.id < rhs.id
        }
        return text0 < text1
    }
    if lhs.text == nil, rhs.text == nil {
        return lhs.id < rhs.id
    }
    return lhs.text != nil
})

同样的方法使用施瓦茨变换后看起来像这样

import SwiftySchwartzianTransform
// ...
func sort(_ records: [Record]) -> [Record] {
    typealias ST = SchwartzianTransform<Record, RecordSortKey>
    let st = ST(records, reverse: false) { (_, record) -> RecordSortKey in
        if let text = record.text?.lowercased() {
            return RecordSortKey.textAndId(text: text, id: record.id)
        } else {
            return RecordSortKey.id(id: record.id)
        }
    }
    print(st)
    return st.result
}
// ...
let output = sort(input)

请参考单元测试代码以获取完整的用例。