我想创建一个泛型函数,根据传递的属性对类数组进行排序。
例如,我有这些类
public class Car {
var id: Int
var manufacturer: String
var variant: String
init(id: Int, manufacturer: String, variant: String) {
self.id = id
self.manufacturer = manufacturer
self.variant = variant
}
}
enum Gender {
case male
case female
}
public class Person {
var id: Int
var name: String
var age: Int
var gender: Gender
init(id: Int, name: String, age: Int, gender: Gender) {
self.id = id
self.name = name
self.age = age
self.gender = gender
}
}
这些数组,
let cars = [
Car(id: 1, manufacturer: "Ford", variant: "Focus"),
Car(id: 2, manufacturer: "Nissan", variant: "Skyline"),
Car(id: 3, manufacturer: "Dodge", variant: "Charger"),
Car(id: 4, manufacturer: "Chevrolet", variant: "Camaro"),
Car(id: 5, manufacturer: "Ford", variant: "Shelby")
]
let persons = [
Person(id: 1, name: "Ed Sheeran", age: 26, gender: .male),
Person(id: 2, name: "Phil Collins", age: 66, gender: .male),
Person(id: 3, name: "Shakira", age: 40, gender: .female),
Person(id: 4, name: "Rihanna", age: 25, gender: .female),
Person(id: 5, name: "Bono", age: 57, gender: .male)
]
如何为数组编写通用扩展,根据传递的属性对其进行排序? (例如,person.sort(名称)或cars.sort(制造商))
谢谢!
答案 0 :(得分:2)
从Swift 4开始,您可以定义一个排序方法 一个Key-Path Expression作为参数。正如Leo所指出的,这些方法可以更普遍地定义为协议扩展方法(分别用于可变集合和序列):
extension MutableCollection where Self: RandomAccessCollection {
// Mutating in-place sort:
mutating func sort<T: Comparable>(byKeyPath keyPath: KeyPath<Element, T>) {
sort(by: { $0[keyPath: keyPath] < $1[keyPath: keyPath] })
}
}
extension Sequence {
// Non-mutating sort, returning a new array:
func sorted<T: Comparable>(byKeyPath keyPath: KeyPath<Element, T>) -> [Element] {
return sorted(by: { $0[keyPath: keyPath] < $1[keyPath: keyPath] })
}
}
使用示例:
persons.sort(byKeyPath: \.name)
cars.sort(byKeyPath: \.manufacturer)
有关密钥路径表达式的更多信息,请参阅SE-0161 Smart KeyPaths: Better Key-Value Coding for Swift。
答案 1 :(得分:2)
在@MartinR answer上扩展以允许增加(<)或减少(>)排序:
extension MutableCollection where Self: RandomAccessCollection {
mutating func sort<T: Comparable>(_ keyPath: KeyPath<Element, T>, by areInIncreasingOrder: ((T, T) -> Bool) = (<)) {
sort(by: { areInIncreasingOrder($0[keyPath: keyPath], $1[keyPath: keyPath]) })
}
}
extension Sequence {
func sorted<T: Comparable>(_ keyPath: KeyPath<Element, T>, by areInIncreasingOrder: ((T,T)-> Bool) = (<)) -> [Element] {
sorted(by: { areInIncreasingOrder($0[keyPath: keyPath], $1[keyPath: keyPath]) })
}
}
people.sorted(\.age)
people.sorted(\.age, by: >)
cars.sorted(\.manufacturer)
cars.sorted(\.manufacturer, by: >)
编辑/更新:
要通过符合Comparable
协议的可选属性来对自定义对象进行排序:
extension MutableCollection where Self: RandomAccessCollection {
mutating func sort<T: Comparable>(_ keyPath: KeyPath<Element, Optional<T>>, by areInIncreasingOrder: ((T, T) -> Bool) = (<)) {
sort(by: {
switch ($0[keyPath: keyPath], $1[keyPath: keyPath]) {
case let (lhs?, rhs?): return areInIncreasingOrder(lhs, rhs)
case (.none, _): return false
case (_, .none): return true
}
})
}
}
extension Sequence {
func sorted<T: Comparable>(_ keyPath: KeyPath<Element, Optional<T>>, by areInIncreasingOrder: ((T,T)-> Bool) = (<)) -> [Element] {
sorted(by: {
switch ($0[keyPath: keyPath], $1[keyPath: keyPath]) {
case let (lhs?, rhs?): return areInIncreasingOrder(lhs, rhs)
case (.none, _): return false
case (_, .none): return true
}
})
}
}
用法:
array.sort(\.optionalStringProperty) {
$0.localizedStandardCompare($1) == .orderedAscending
}
print(array)
答案 2 :(得分:1)
你走了:
extension Array {
mutating func propertySort<T: Comparable>(_ property: (Element) -> T) {
sort(by: { property($0) < property($1) })
}
}
用法:
persons.propertySort({$0.name})
这是一个非变异版本:
func propertySorted<T: Comparable>(_ property: (Element) -> T) -> [Element] {
return sorted(by: {property($0) < property($1)})
}
正如Leo Dabus指出的那样,您可以将扩展名推广到MutableCollection
的任何RandomAccessCollection
:
extension MutableCollection where Self : RandomAccessCollection {
...