在 Swift 5.9 中,您可以使用参数包编写这样的方法。这里我使用内置的排序来实现KeyPathComparator
:
extension Array {
mutating func sort<each T: Comparable>(by keyPaths: repeat (KeyPath<Element, each T>, SortOrder)) {
var comparators: [KeyPathComparator<Element>] = []
func addComparator<Key: Comparable>(_ keyPathOrder: (KeyPath<Element, Key>, SortOrder)) {
comparators.append(KeyPathComparator(keyPathOrder.0, order: keyPathOrder.1))
}
// here I am technically creating a tuple, with its elements all being addComparator calls
// this could be written more readably when we can iterate over a parameter pack with a for loop
(repeat addComparator(each keyPaths))
sort(using: comparators)
}
}
用法示例:
// order by length of string ascendingly, then by the string itself descendingly
someStrings.sort(by: (\.count, .forward), (\.self, .reverse))
在 Swift 5.9 之前,您需要编写AnyComparable
类型橡皮擦。这个是根据这篇文章改编的here https://stackoverflow.com/questions/68878549/class-anycomparable-inherits-from-class-anyequatable/68878884#68878884.
struct AnyComparable: Equatable, Comparable {
private let lessThan: (Any) -> Bool
private let value: Any
private let equals: (Any) -> Bool
public static func == (lhs: AnyComparable, rhs: AnyComparable) -> Bool {
lhs.equals(rhs.value) || rhs.equals(lhs.value)
}
public init<C: Comparable>(_ value: C) {
self.value = value
self.equals = { $0 as? C == value }
self.lessThan = { ($0 as? C).map { value < $0 } ?? false }
}
public static func < (lhs: AnyComparable, rhs: AnyComparable) -> Bool {
lhs.lessThan(rhs.value) || (rhs != lhs && !rhs.lessThan(lhs.value))
}
}
这样,你就可以写你的sort
方法签名为:
mutating func sort(by criteria: (path: KeyPath<Element, AnyComparable>, order:OrderType)...) {
}
为了让我们更容易用类型传递关键路径AnyComparable
中,我们可以进行扩展:
extension Comparable {
// this name might be too long, but I'm sure you can come up with a better name
var anyComparable: AnyComparable {
.init(self)
}
}
现在我们可以这样做:
someArray.sort(by: (\.key1.anyComparable, .asc), (\.key2.anyComparable, .asc))