【问题标题】:Sorting a Custom Object by different property values in Swift在 Swift 中按不同的属性值对自定义对象进行排序
【发布时间】:2017-11-27 17:44:16
【问题描述】:

我正在尝试轻松地按不同的属性值对自定义结构数组进行排序。

struct Customer: Comparable, Equatable {
    var name: String
    var isActive: Bool
    var outstandingAmount: Int
    var customerGroup: String
}

var customerlist: [Customer] // This is downloaded from our backend. 

当用户选择各种图标时,我希望能够按所有字段值对 UI 中的 customerlist 数组进行排序。

我尝试了一些方法来使用 switch 语句对其进行排序 - 但是我被告知正确的方法是使用排序描述符(它似乎是基于 Objective-C 的,意味着我需要将我的数组转换为一个 NSArray。)当我尝试使用本机 Swift 结构的这种方法时,我不断收到错误。

允许用户使用 Swift 对上述数组进行排序的最佳方式是什么?

例如:下面看起来很冗长!

func sortCustomers(sortField:ColumnOrder, targetArray:[Customer]) -> [Customer] { //Column Order is the enum where I have specified the different possible sort orders
        var result = [Customer]()
    switch sortField {
        case .name:
             result = targetArray.sorted(by: { (cust0: Customer, cust1: Customer) -> Bool in
                return cust0.name > cust1.name
            })
        case .isActive:
             result = targetArray.sorted(by: { (cust0: Customer, cust1: Customer) -> Bool in
                return cust0.isActive > cust1.isActive
            })
        case .outstandingAmount:
            result = targetArray.sorted(by: { (cust0: Customer, cust1: Customer) -> Bool in
                return cust0.outstandingAmount > cust1.outstandingAmount
            })
        case .customerGroup:
            result = targetArray.sorted(by: { (cust0: Customer, cust1: Customer) -> Bool in
                return cust0.customerGroup > cust1.customerGroup
            })
    }
    return result
}

【问题讨论】:

  • 您可以使用位置参数而不是给它们命名来简化闭包(例如,案例 1:targetArray.sorted { $0.name > $0.name }
  • 我认为排序描述符是正确的方法。 - 但似乎没有工作

标签: arrays swift sorting nssortdescriptor


【解决方案1】:

我会选择使用 KeyPaths:

func sortCustomers<T: Comparable>(customers: [Customer], with itemPath: KeyPath<Customer, T>) -> [Customer] {
    return customers.sorted() {
       $0[keyPath: itemPath] < $1[keyPath: itemPath]
    }
}

这种方法完全不需要你的枚举,并且允许你做

let testData = [Customer(name: "aaaa", isActive: false, outstandingAmount: 1, customerGroup: "aaa"),
                Customer(name: "bbbb", isActive: true, outstandingAmount: 2, customerGroup: "bbb")];

let testResultsWithName = sortCustomers(customers: testData, with: \Customer.name)
let testResultsWithActive = sortCustomers(customers: testData, with: \Customer.isActive) 
// etc

请注意,我将 &gt; 切换为 &lt;。这是默认期望,将导致“a”在“b”之前,“1”在“2”之前,等等。

另外,您需要为 Bool 添加一个扩展以进行比较:

extension Bool: Comparable {
    public static func <(lhs: Bool, rhs: Bool) -> Bool {
        return lhs == rhs || (lhs == false && rhs == true)
    }
}

为了完善该方法,您还可以传入一个比较函数:

func sortCustomers<T: Comparable>(customers: [Customer], comparing itemPath: KeyPath<Customer, T>, using comparitor: (T, T) -> Bool) -> [Customer] {
    return customers.sorted() {
        comparitor($0[keyPath: itemPath], $1[keyPath: itemPath])
    }
}
let testResults = sortCustomers(customers: testData, comparing: \Customer.name, using: <)

这样您可以使用普通的比较运算符:(, >=) 以及如果您想要自定义排序的闭包。

【讨论】:

  • 真漂亮?
  • 对吗? KP 太棒了!
  • 太棒了,但我认为 NSSortDescriptors 是正确的方法 - 因为您还可以调整对象数组的升序与降序。 - 只是似乎不起作用
  • @MobileBloke 添加了一个传递比较函数的示例,因此您可以在使用点控制比较。归根结底,这更多的是方法上的差异。 NSSortDescriptors 更基于 Type/OOP,我的方法更实用。一个并不比另一个特别好。
【解决方案2】:

我重新打包了详细的解决方案以使事情变得更好。我向ColumnOrder 添加了一个属性,该属性返回一个排序闭包。

struct Customer {
    var name: String
    var isActive: Bool
    var outstandingAmount: Int
    var customerGroup: String
}

enum ColumnOrder {
    case name
    case isActive
    case outstandingAmount
    case customerGroup

    var ordering: (Customer, Customer) -> Bool {
        switch self {
        case .name:              return { $0.name > $1.name }
        case .isActive:          return { $0.isActive && !$1.isActive }
        case .outstandingAmount: return { $0.outstandingAmount > $1.outstandingAmount}
        case .customerGroup:     return { $0.customerGroup > $1.customerGroup }
        }
    }
}

这是它的使用方法:

let sortedCustomers = customers.sorted(by: ColumnOrder.name.ordering)

接下来,我扩展了 Sequence 以使从数组中调用它看起来不错。

extension Sequence where Element == Customer {
    func sorted(by columnOrder: ColumnOrder) -> [Element] {
        return sorted(by: columnOrder.ordering)
    }
}

最终结果:

let sortedCustomers = customers.sorted(by: .name)

【讨论】:

    猜你喜欢
    • 2011-02-16
    • 1970-01-01
    相关资源
    最近更新 更多