我正在处理 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
block 。这是一个准确的评估吗?
更新:
这是我的 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
}
}
最佳答案
如何添加一个 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:
方法以返回一个Comparable
或其他。
乍一看,我意识到 Key
的 Enum
看起来有点矫枉过正,但我认为在选择属性和排序时最好只处理有效的键,并在您第一次从 sortDescriptor 获取 key 时处理无效输入的可能性。
编辑:由于您为属性使用了不同的多种类型,因此制作一个基于键比较 2 个 Payment
实例的函数可能会更容易,并使用排序。我更新后的实现如下所示:
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: String
func compare(toOther other: Payment, byKey key: Key, ascending: Bool) -> Bool {
switch key {
case .id:
return ascending ? self.id < other.id : self.id > other.id
case .amount:
return ascending ? self.amount < other.amount : self.amount > other.amount
case .createdAt:
return ascending ? self.createdAt < other.createdAt : self.createdAt > other.createdAt
case .status:
return ascending ? self.status < other.status : self.status > other.status
}
}
}
这将使您的排序看起来像这样:
let sortDescriptor = NSSortDescriptor(key: "amount", ascending: true)
let key = Key(rawValue: sortDescriptor.key!)!
paymentList.sort(by: { $0.compare(toOther: $1, byKey: key, ascending: sortDescriptor.ascending) })
关于swift - 如何简化这个 Swift 排序代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48511745/