Swift -具有布尔值和可选日期的人员排序数组



我想对考虑3个标准的人员数组进行排序:这个数组由三个信息组成:

  1. 名称(字符串)
  2. hasPurchased (Bool)
  3. purchaseDate(如果haspurchase = false,可以为nil)

我可以按名字对我的客户进行排序,没有问题:

customersList.sort(by: {$0.name!.compare($1.name!) == .orderedAscending})

我可以按布尔值和名称排序,没有问题:

customersList.sort {$1.hasPurchased == $0.hasPurchased ? ($0.name!.compare($1.name!) == .orderedAscending) : $1.hasPurchased && !$0.hasPurchased}

但是我想做的是对一个数组进行排序:

  1. 首先,通过布尔值和名称(haspurchase = false, name orderedAscending)
  2. 然后,如果hasbought = true,按purchaseDate排序,比较日期顺序递增

这样,我将按"ABC"保存在一个数组中。还没有购买的,然后按购买日期排序的(在本例中为非空条件)…

告诉我你是怎么想的。

注意,sort的参数名为" areinincreingorder "。给定两个Customer对象,您应该返回它们是否按递增顺序排列。因此,要确定两个客户是否按递增顺序使用您的2条规则:

  • 如果它们具有不同的hasPurchased值,则如果第二个已购买而第一个未购买,则它们依次递增
  • 否则,如果它们的hasPurchased值与false相同,则如果第一个名称与第二个名称按递增顺序
  • 否则,如果它们的hasPurchased值与true值相同,则如果第一个的购买日期与第二个的购买日期在递增顺序中

翻译成代码:

customerList.sort { c1, c2 in
if c1.hasPurchased != c2.hasPurchased {
// non-purchased customers are ordered before purchased customers
return c2.hasPurchased && !c1.hasPurchased
} else if !c1.hasPurchased { // both are non-purchased customers
// sort by name
return c1.name < c2.name
} else { // both are purchased customers
// sort by date
return c1.purchaseDate! < c2.purchaseDate!
}
}

如果这是您想要排序客户的默认和最普遍的方式,我会通过定义Comparable让客户考虑到这些优先级来实现这一点。

extension Customer: Comparable {
static func < (lhs: Customer, rhs: Customer) -> Bool {
switch (lhs.hasPurchased, rhs.hasPurchased) {
case (true, true): return lhs.purchaseDate! < rhs.purchaseDate!
case (true, false): return false
case (false, true): return true
case (false, false): return lhs.name < rhs.name
}
}
}

这将允许您使用Sequence:

中的.sorted()方法。
customers.sorted()

举个例子:

let customers:[Customer] = [
Customer(name: "Alan", hasPurchased: false, purchaseDate: nil),
Customer(name: "Ben", hasPurchased: false, purchaseDate: nil),
Customer(name: "Carol", hasPurchased: true, purchaseDate: Date()),
Customer(name: "Dan", hasPurchased: true, purchaseDate: Date().addingTimeInterval(100)),
Customer(name: "Elsie", hasPurchased: false, purchaseDate: nil),
Customer(name: "Frank", hasPurchased: true, purchaseDate: Date().addingTimeInterval(200))
]
let s = customers.sorted()
s.map{print($0)}

等于:

Customer(name: "Alan", hasPurchased: false, purchaseDate: nil)
Customer(name: "Ben", hasPurchased: false, purchaseDate: nil)
Customer(name: "Elsie", hasPurchased: false, purchaseDate: nil)
Customer(name: "Carol", hasPurchased: true, purchaseDate: Optional(2021-08-09 11:02:55 +0000))
Customer(name: "Dan", hasPurchased: true, purchaseDate: Optional(2021-08-09 11:04:35 +0000))
Customer(name: "Frank", hasPurchased: true, purchaseDate: Optional(2021-08-09 11:06:15 +0000))

这种方法意味着,如果你想在其他地方,例如,只是按名称排序,那么你将不得不通过闭包显式地定义该排序,但由于排序闭包将更容易编写(稍后理解),我认为这是一个可以接受的妥协。这取决于更广泛用例的细节,从问题中不清楚。如果这种需求是例外而非规范,则可以采用上述方法实现显式排序闭包。

hasPurchasedBool为冗余信息。对象要么有购买日期,要么没有。就目前情况而言,如果存在日期但hasPurchased为假,您将如何处理?如果haspurchase是真实的,但是没有日期存在怎么办?我建议去掉Bool

我想这样解决这个问题:

struct Person {
let name: String
let purchaseDate: Date?
}
func example(people: [Person]) -> [Person] {
people.sorted(by: unpurchasedFirst)
}
func unpurchasedFirst(lhs: Person, rhs: Person) -> Bool {
switch (lhs.purchaseDate, rhs.purchaseDate) {
case (.none, .some): // lhs hasn't purchased yet, so needs to go first
return true
case (.some, .none): // rhs hasn't purchased yet, so needs to go first
return false
case let (.some(lhsDate), .some(rhsDate)): // both purchased, so most recent purchase first
return lhsDate < rhsDate
case (.none, .none): // neither purchased, so ABC order by name
return lhs.name.caseInsensitiveCompare(rhs.name) == .orderedAscending
}
}

switch语句和注释非常清楚地表明顺序是什么。排序是在一个单独的函数中,这使得测试非常容易。

最新更新