如何简化此Swift排序代码?

时间:2018-01-29 23:35:38

标签: swift sorting

我正在使用我自己的数据tutorial on NSTableView。我已经达到了编写排序功能的程度,这就是我想出来的:

var payment_list: [Payment]!

func tableView(_ tableView: NSTableView, sortDescriptorsDidChange oldDescriptors: [NSSortDescriptor]) {
    guard let sortDescriptor = tableView.sortDescriptors.first else {
        return
    }

    let key = sortDescriptor.key!

    if sortDescriptor.ascending == true {
        if key == "id" {
            payment_list.sort { $0.id < $1.id }
        } else if key == "msatoshi" {
            payment_list.sort { $0.msatoshi < $1.msatoshi }
        } else if key == "created_at" {
            payment_list.sort { $0.created_at < $1.created_at }
        } else if key == "status" {
            payment_list.sort { $0.status < $1.status }
        }
    } else {
        if key == "id" {
            payment_list.sort(by: { $0.id > $1.id })
        } else if key == "msatoshi" {
            payment_list.sort { $0.msatoshi > $1.msatoshi }
        } else if key == "created_at" {
            payment_list.sort { $0.created_at > $1.created_at }
        } else if key == "status" {
            payment_list.sort { $0.status > $1.status }
        }
    }

    tableView.reloadData()
}

它运行正常,但我不禁想到这对于排序多列列表这样常见的东西非常冗长。我的应用中的实际列表还有几个附加列。

我是否可以使用任何Swift技巧使这段代码更简洁?这样的事情会更令人满意:

if sortDescriptor.ascending == true {
    payment_list.sort($0[key] < $1[key])
} else {
    payment_list.sort($0[key] > $1[key])
}

或者甚至可能是:

payment_list = payment_list.sort(by: key, order: "ASC")

我的印象是Swift开发涉及很多嵌套的if...else if和/或switch...case块。这是一个准确的评估吗?

更新

这是我的Payment定义:

struct Payment: Codable, PaymentFactory {
    let id: Int
    let payment_hash: String
    let destination: String
    let msatoshi: Int
    let timestamp: Int
    let created_at: Int
    let status: String

    enum PaymentKeys: String, CodingKey {
        case payments
    }

    static func fake() -> Payment {
        // snipped for brevity
    }
}

2 个答案:

答案 0 :(得分:1)

如何添加Enum来处理各种排序描述符键?然后,您可以向Payment类添加一个函数,该函数根据传入的枚举值返回正确的属性,这使您的实际排序函数非常简洁。

例如,如果您的所有属性都是Int,则可以执行以下操作:

enum Key: String {
    case id = "id", amount = "amount", createdAt = "createdAt", status = "status"
}

class Payment {

  let id: Int
  let amount: Int
  let createdAt: Int
  let status: Int

  func attribute(forKey key: Key) -> Int {
    switch key {
    case .id: return self.id
    case .amount: return self.amount
    case .createdAt: return self.createdAt
    case .status: return self.status
    }
  }

}

然后,当需要排序时,你可以这样做:

let key = Key(rawValue: sortDescriptor.key!)!

if sortDescriptor.ascending {
  paymentList.sort(by: { $0.attribute(forKey: key) < $1.attribute(forKey: key) })
} else {
  paymentList.sort(by: { $0.attribute(forKey: key) > $1.attribute(forKey: key) })
}

或者您甚至可以使用三元运算符将排序部分转换为单行:

paymentList.sort(by: { sortDescriptor.ascending ? $0.attribute(forKey: key) < $1.attribute(forKey: key) : $0.attribute(forKey: key) > $1.attribute(forKey: key) })

当然,如果您的属性属于不同类型,这会变得稍微复杂一些,但这种方法仍然可以使用 - 您真的只需更改attribute(forKey:方法即可返回{{1}或其他东西。

乍一看,我发现Comparable的{​​{1}}看起来有点矫枉过正,但我​​认为在挑选属性和排序时只处理有效的密钥会更好,并且处理在您第一次从sortDescriptor获取密钥时输入无效的可能性。

编辑:由于您为属性使用了不同的多种类型,因此根据键创建一个比较2 Enum个实例的函数可能会更容易,并使用它来排序。我更新的实现如下所示:

Key

这会使你的排序看起来像这样:

Payment

答案 1 :(得分:0)

如果您的Payment类型为NSObject,则可以使用@objc标记数据字段,并使用NSSortDescriptor内置的比较功能。它甚至会自动处理上升/下降。

工作游乐场代码段:

import Foundation

class Payment: NSObject {
    @objc var id: String
    @objc var amount: Double
    @objc var created_at: Date
    @objc var status: String

    init(id: String, amount: Double, created_at: Date, status: String) {
        self.id = id
        self.amount = amount
        self.created_at = created_at
        self.status = status
        super.init()
    }
}

let arr: [Payment] = [
    Payment(id: "1", amount: 123, created_at: Date(), status: "abc"),
    Payment(id: "5", amount: 123, created_at: Date(), status: "abc"),
    Payment(id: "4", amount: 123, created_at: Date(), status: "abc"),
    Payment(id: "2", amount: 123, created_at: Date(), status: "abc")
]

let sd = NSSortDescriptor(key: "id", ascending: true)

let sorted = arr.sorted { sd.compare($0, to: $1) == .orderedAscending }

dump(sorted)