タプルを使用して複数の基準を比較する
複数の条件でソートを実行するのは本当に簡単な方法(すなわち、1つの比較によって、および同等の場合は、別の比較でソート)が使用しているタプルを通り、<
そして>
事業者は辞書式の比較を行う彼らのために過負荷を持っています。
/// 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
例えば:
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")
// ]
これはlastName
最初に要素のプロパティを比較します。それらが等しくない場合、ソート順は<
それらとの比較に基づきます。それらが等しい場合、タプル内の次の要素のペアに移動しfirstName
ます。つまり、プロパティを比較します。
標準ライブラリは<
、>
2から6要素のタプルのオーバーロードを提供します。
プロパティごとに異なる並べ替え順序が必要な場合は、タプル内の要素を単純に交換できます。
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"),
// ]
これで、lastName
降順、firstName
昇順で並べ替えられます。
sort(by:)
複数の述語をとるオーバーロードの定義
上の議論に触発さでソートコレクションmap
閉鎖やSortDescriptors、別のオプションは、カスタムの過負荷を定義することであろうsort(by:)
とsorted(by:)
、各述語は、要素の順序を決定するために順番に考えられている-複数の述語とその取引を。
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:
パラメーターは残念ですが、既存のsort(by:)
オーバーロードで曖昧さが生じないようにするために必要です)
これにより、(contacts
前の配列を使用して)次のように言うことができます。
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
// ...
)
呼び出しサイトはタプルバリアントほど簡潔ではありませんが、比較対象と順序を明確にすることができます。
に準拠 Comparable
この種の比較を定期的に行う場合は、@ AMomchilovと@appzYourLifeが示唆するよう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)
}
}
そして今sort()
、昇順を要求するだけです:
contacts.sort()
またはsort(by: >)
降順の場合:
contacts.sort(by: >)
ネストされたタイプでのカスタムソート順の定義
使用したい他のソート順がある場合は、ネストされたタイプで定義できます。
extension Contact {
enum Comparison {
static let firstLastAscending: (Contact, Contact) -> Bool = {
return ($0.firstName, $0.lastName) <
($1.firstName, $1.lastName)
}
}
}
そして単に次のように呼び出します:
contacts.sort(by: Contact.Comparison.firstLastAscending)