swift - 如何简化这个 Swift 排序代码?

标签 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 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 或其他。

乍一看,我意识到 KeyEnum 看起来有点矫枉过正,但我​​认为在选择属性和排序时最好只处理有效的键,并在您第一次从 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/

相关文章:

Swift:如何在 NSURL 中引用本地镜像

swift - 如何重置 UICollectionVIewCell 的背景颜色

java - 用Java对二维数组排序-跳过第一个索引

java - Collections.sort 不是排序

python - 对工作日文本进行排序

ios - Swift:动画后将 UIView 的框架重置为其约束

ios - 由于 "nil"参数导致 XCTestAssertNil 崩溃

arrays - Swift 2 NSUserDefaults 读取数组

python - 根据另一个字符串列表对字符串列表进行排序

sorting - 如何在不声明新数据的情况下更改类型(String,Int)元组的 Ord 实例?