我正在使用我自己的数据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
}
}
答案 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)