I'd like to sort an array of persons considering 3 criteria : this array is made of customers with 3 informations :
I'm able to sort my customers by name, no problem:
customersList.sort(by: {$0.name!.compare($1.name!) == .orderedAscending})
I'm able to sort by boolean and name, no problem:
customersList.sort {$1.hasPurchased == $0.hasPurchased ? ($0.name!.compare($1.name!) == .orderedAscending) : $1.hasPurchased && !$0.hasPurchased}
But what I would like to do is to sort in a single array :
This way, I would keep in a single array my customers by "ABC" that haven't purchased yet and then those who purchased sorted by date of purchase (non-null criteria in this case)...
tell me what you think about it.
Notice that the argument to sort is called "areInIncreasingOrder". Given two Customer objects, you are supposed to return whether they are in increasing order. So to determine whether two customers are in increasing order with your 2 rules:
hasPurchased values, they are in increasing order if the second one has purchased and the first one has nothasPurchased value of false, then they are in increasing order if the first one's name is in increasing order with the second one's namehasPurchased value of true, then they are in increasing order if the first one's purchase date is in increasing order with the second one's purchase dateTranslating that to code:
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!
}
}
If this will be the default and most prevalent way you want want to sort customers by I'd approach this by defining Comparable for the customer to take into account these priorities.
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
}
}
}
This will then allow you to just use the .sorted() method from Sequence:
customers.sorted()
As an examples of this:
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)}
Which gives you:
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))
This approach will mean that if you want at some other point, for example, to just sort by name, then you will have to define that sort explicitly via a closure, but as that sort closure will be far simpler to write (and later on to understand) I see this as an acceptable compromise. It depends on the detail of your wider use case, which isn't clear from the question. If this need is the exception rather than the norm, you can adopt the above approach to an explicit sort closure.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With