Sử dụng bộ giá trị để so sánh nhiều tiêu chí
Một cách thực sự đơn giản để thực hiện sắp xếp theo nhiều tiêu chí (tức là sắp xếp theo một phép so sánh và nếu tương đương thì bằng một phép so sánh khác) là sử dụng các bộ giá trị , vì toán tử <
và >
có quá tải cho chúng để thực hiện so sánh từ vựng.
/// Returns a Boolean value indicating whether the first tuple is ordered
/// before the second in a lexicographical ordering.
///
/// Given two tuples `(a1, a2, ..., aN)` and `(b1, b2, ..., bN)`, the first
/// tuple is before the second tuple if and only if
/// `a1 < b1` or (`a1 == b1` and
/// `(a2, ..., aN) < (b2, ..., bN)`).
public func < <A : Comparable, B : Comparable>(lhs: (A, B), rhs: (A, B)) -> Bool
Ví dụ:
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)
// [
// Contact(firstName: "Charles", lastName: "Alexson"),
// Contact(firstName: "Leonard", lastName: "Charleson"),
// Contact(firstName: "Alex", lastName: "Elexson"),
// Contact(firstName: "Michael", lastName: "Elexson"),
// Contact(firstName: "Michael", lastName: "Webb")
// ]
Điều này sẽ so sánh các lastName
thuộc tính của các phần tử đầu tiên. Nếu chúng không bằng nhau, thì thứ tự sắp xếp sẽ dựa trên sự <
so sánh với chúng. Nếu họ là bằng nhau, sau đó nó sẽ chuyển sang cặp tiếp theo của các nguyên tố trong các tuple, tức là so sánh các firstName
thuộc tính.
Thư viện chuẩn cung cấp <
và >
nạp chồng cho các bộ giá trị từ 2 đến 6 phần tử.
Nếu bạn muốn các thứ tự sắp xếp khác nhau cho các thuộc tính khác nhau, bạn chỉ cần hoán đổi các phần tử trong các bộ giá trị:
contacts.sort {
($1.lastName, $0.firstName) <
($0.lastName, $1.firstName)
}
// [
// Contact(firstName: "Michael", lastName: "Webb")
// Contact(firstName: "Alex", lastName: "Elexson"),
// Contact(firstName: "Michael", lastName: "Elexson"),
// Contact(firstName: "Leonard", lastName: "Charleson"),
// Contact(firstName: "Charles", lastName: "Alexson"),
// ]
Điều này bây giờ sẽ sắp xếp theo lastName
giảm dần, sau đó firstName
tăng dần.
Xác định sort(by:)
quá tải có nhiều vị từ
Lấy cảm hứng từ cuộc thảo luận về Sắp xếp Bộ sưu tập với bao map
đóng và Bộ ký hiệu sắp xếp , một tùy chọn khác sẽ là xác định quá tải tùy chỉnh sort(by:)
và sorted(by:)
xử lý nhiều vị từ - trong đó mỗi vị từ được xem xét lần lượt để quyết định thứ tự của các phần tử.
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:
Tham số là không may, nhưng được yêu cầu để tránh tạo ra sự mơ hồ với sort(by:)
tình trạng quá tải hiện có )
Sau đó, điều này cho phép chúng tôi nói (sử dụng contacts
mảng trước đó):
contacts.sort(by:
{ $0.lastName > $1.lastName }, // first sort by lastName descending
{ $0.firstName < $1.firstName } // ... then firstName ascending
// ...
)
print(contacts)
// [
// Contact(firstName: "Michael", lastName: "Webb")
// Contact(firstName: "Alex", lastName: "Elexson"),
// Contact(firstName: "Michael", lastName: "Elexson"),
// Contact(firstName: "Leonard", lastName: "Charleson"),
// Contact(firstName: "Charles", lastName: "Alexson"),
// ]
// or with sorted(by:)...
let sortedContacts = contacts.sorted(by:
{ $0.lastName > $1.lastName }, // first sort by lastName descending
{ $0.firstName < $1.firstName } // ... then firstName ascending
// ...
)
Mặc dù call-site không ngắn gọn như biến thể tuple, nhưng bạn sẽ hiểu rõ hơn về những gì đang được so sánh và theo thứ tự.
Tuân theo Comparable
Nếu bạn đang đi để được làm các loại so sánh thường xuyên sau đó, như @AMomchilov & @appzYourLife gợi ý, bạn có thể phù hợp Contact
để 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)
}
}
Và bây giờ chỉ cần gọi sort()
cho một đơn đặt hàng tăng dần:
contacts.sort()
hoặc sort(by: >)
cho một thứ tự giảm dần:
contacts.sort(by: >)
Xác định thứ tự sắp xếp tùy chỉnh trong một loại lồng nhau
Nếu bạn có các thứ tự sắp xếp khác mà bạn muốn sử dụng, bạn có thể xác định chúng theo kiểu lồng nhau:
extension Contact {
enum Comparison {
static let firstLastAscending: (Contact, Contact) -> Bool = {
return ($0.firstName, $0.lastName) <
($1.firstName, $1.lastName)
}
}
}
và sau đó chỉ cần gọi là:
contacts.sort(by: Contact.Comparison.firstLastAscending)