Menggunakan tupel untuk melakukan perbandingan beberapa kriteria
Cara yang sangat sederhana untuk melakukan pengurutan menurut beberapa kriteria (yaitu mengurutkan berdasarkan satu perbandingan, dan jika setara, kemudian dengan perbandingan lain) adalah dengan menggunakan tupel , karena operator <
dan >
memiliki kelebihan beban untuk mereka yang melakukan perbandingan leksikografik.
public func < <A : Comparable, B : Comparable>(lhs: (A, B), rhs: (A, B)) -> Bool
Sebagai contoh:
struct Contact {
var firstName: String
var lastName: String
}
var contacts = [
Contact(firstName: "Leonard", lastName: "Charleson"),
Contact(firstName: "Michael", lastName: "Webb"),
Contact(firstName: "Charles", lastName: "Alexson"),
Contact(firstName: "Michael", lastName: "Elexson"),
Contact(firstName: "Alex", lastName: "Elexson"),
]
contacts.sort {
($0.lastName, $0.firstName) <
($1.lastName, $1.firstName)
}
print(contacts)
Ini akan membandingkan properti elemen lastName
terlebih dahulu. Jika tidak sama, maka urutan sortir akan didasarkan pada <
perbandingan dengannya. Jika mereka adalah sama, maka akan pindah ke pasangan berikutnya elemen dalam tupel, yaitu membandingkan firstName
sifat.
Pustaka standar menyediakan <
dan >
membebani tupel dengan 2 hingga 6 elemen.
Jika Anda menginginkan urutan pengurutan yang berbeda untuk properti yang berbeda, Anda cukup menukar elemen di tupel:
contacts.sort {
($1.lastName, $0.firstName) <
($0.lastName, $1.firstName)
}
Ini sekarang akan mengurutkan berdasarkan lastName
turun, lalu firstName
naik.
Mendefinisikan sort(by:)
kelebihan beban yang membutuhkan banyak predikat
Terinspirasi oleh diskusi tentang Menyortir Koleksi dengan map
closures dan SortDescriptors , opsi lain adalah menentukan kelebihan beban khusus sort(by:)
dan sorted(by:)
yang berhubungan dengan beberapa predikat - di mana setiap predikat dipertimbangkan secara bergiliran untuk memutuskan urutan elemen.
extension MutableCollection where Self : RandomAccessCollection {
mutating func sort(
by firstPredicate: (Element, Element) -> Bool,
_ secondPredicate: (Element, Element) -> Bool,
_ otherPredicates: ((Element, Element) -> Bool)...
) {
sort(by:) { lhs, rhs in
if firstPredicate(lhs, rhs) { return true }
if firstPredicate(rhs, lhs) { return false }
if secondPredicate(lhs, rhs) { return true }
if secondPredicate(rhs, lhs) { return false }
for predicate in otherPredicates {
if predicate(lhs, rhs) { return true }
if predicate(rhs, lhs) { return false }
}
return false
}
}
}
extension Sequence {
mutating func sorted(
by firstPredicate: (Element, Element) -> Bool,
_ secondPredicate: (Element, Element) -> Bool,
_ otherPredicates: ((Element, Element) -> Bool)...
) -> [Element] {
return sorted(by:) { lhs, rhs in
if firstPredicate(lhs, rhs) { return true }
if firstPredicate(rhs, lhs) { return false }
if secondPredicate(lhs, rhs) { return true }
if secondPredicate(rhs, lhs) { return false }
for predicate in otherPredicates {
if predicate(lhs, rhs) { return true }
if predicate(rhs, lhs) { return false }
}
return false
}
}
}
( secondPredicate:
Parameter ini disayangkan, tetapi diperlukan untuk menghindari membuat ambiguitas dengan sort(by:)
kelebihan beban yang ada )
Ini kemudian memungkinkan kita untuk mengatakan (menggunakan contacts
array dari sebelumnya):
contacts.sort(by:
{ $0.lastName > $1.lastName },
{ $0.firstName < $1.firstName }
)
print(contacts)
let sortedContacts = contacts.sorted(by:
{ $0.lastName > $1.lastName },
{ $0.firstName < $1.firstName }
)
Meskipun situs panggilan tidak sesingkat varian tupel, Anda mendapatkan kejelasan tambahan dengan apa yang dibandingkan dan dalam urutan apa.
Sesuai dengan Comparable
Jika Anda akan melakukan perbandingan semacam ini secara teratur, seperti yang disarankan @AMomchilov & @appzYourLife , Anda dapat menyesuaikan diri Contact
dengan Comparable
:
extension Contact : Comparable {
static func == (lhs: Contact, rhs: Contact) -> Bool {
return (lhs.firstName, lhs.lastName) ==
(rhs.firstName, rhs.lastName)
}
static func < (lhs: Contact, rhs: Contact) -> Bool {
return (lhs.lastName, lhs.firstName) <
(rhs.lastName, rhs.firstName)
}
}
Dan sekarang panggil saja sort()
untuk urutan naik:
contacts.sort()
atau sort(by: >)
untuk urutan menurun:
contacts.sort(by: >)
Mendefinisikan urutan kustom dalam tipe bertingkat
Jika Anda memiliki susunan urutan lain yang ingin Anda gunakan, Anda dapat menentukannya dalam tipe bertingkat:
extension Contact {
enum Comparison {
static let firstLastAscending: (Contact, Contact) -> Bool = {
return ($0.firstName, $0.lastName) <
($1.firstName, $1.lastName)
}
}
}
lalu panggil sebagai:
contacts.sort(by: Contact.Comparison.firstLastAscending)