代码之家  ›  专栏  ›  技术社区  ›  UKDataGeek

在Swift中按不同属性值对自定义对象排序

  •  0
  • UKDataGeek  · 技术社区  · 8 年前

    我试图通过不同的属性值轻松地对自定义结构数组进行排序。

    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
    }
    
    2 回复  |  直到 8 年前
        1
  •  2
  •   GetSwifty    8 年前

    我将使用关键路径:

    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
    

    注意,我切换了 > 到a < . 这是默认预期,将导致“b”之前的“a”,“2”之前的“1”等。

    此外,您需要为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: <)
    

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

        2
  •  2
  •   Jeffery Thomas    8 年前

    我重新打包了冗长的解决方案,使其变得更好。我将属性添加到 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)