簡體   English   中英

在 Swift 中按屬性對類或結構數組進行排序的通用函數

[英]Generic function to sort array of class or struct by properties in Swift

我想創建一個通用函數來根據傳遞的屬性對類數組進行排序。

例如,我有這些課程

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)
]

如何為數組編寫通用擴展,根據傳遞的屬性對其進行排序? (例如persons.sort(name) 或cars.sort(manufacturer))

謝謝!

給你:

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 所指出的,您可以將擴展推廣到任何同時也是RandomAccessCollection MutableCollection

extension MutableCollection where Self : RandomAccessCollection {
    ...

從 Swift 4 開始,您可以定義一個將Key-Path 表達式作為參數的排序方法。 正如 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

擴展@MartinR答案以允許增加 (<) 或減少 (>) 排序:


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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM