seq.sorted(_:)
sort a sequence on multiple properties
有了下面的擴充後,我們就可以用這麼漂亮的語法來對物件排序:
let sorted = items.sorted(
.descending(\.price),
.descending(\.rate),
.ascending (\.title)
)
👉 replit
// 1.0.0: 2021.12.11
/*
設計理念:
--------
我們將要用於排序的屬性 key path (`prop`) 傳入 `SortOrder`
的 `isBefore` 與 `isEqual` 兩個 closure 中,如此就可以
直接用 order.isBefore(a, b) 或 order.isEqual(a, b) 來
比較兩個 Element 的大小,而不需要為了要將這個 key path 存起來
而傷腦筋到底要用: KeyPath<Element, Property> 還是要用
PartialKeyPath<Element> 型別?
事實上,用這兩種型別存起來都有問題。
首先,如果用 KeyPath<Element, Property>,這時勢必要將 SortOrder
的型別也改為 SortOrder<Element, Property>,但這樣一改,後面
sorted() 函數的 variadic 參數 orders 也要跟著改,這會造成
所有的屬性都是同一種 Property 型別,直接讓 sorted() 函數無法使用。
再來,如果用 PartialKeyPath<Element> 來存 key path,這時
這個 key path 會失去 (type-erase) 它的 Value type,間接
造成系統無法辨別 element[keyPath: keyPath] 的型別,因此也
不知道原來這個值是一個 Comparable,所以在程式中也沒辦法用 "==",
"<", ">" 這些運算符號,因此就算將這個 key path 存起來也沒什麼用。
*/
/// SortOrder
public struct SortOrder<Element> {
public let isBefore: (Element, Element) -> Bool
public let isEqual : (Element, Element) -> Bool
}
// ⭐️ SortOrder
extension SortOrder {
/// .ascending(\.property)
public static func ascending<Property: Comparable>(
_ prop: KeyPath<Element, Property>
) -> SortOrder<Element>
{
return SortOrder(
isBefore: { $0[keyPath: prop] < $1[keyPath: prop] },
isEqual : { $0[keyPath: prop] == $1[keyPath: prop] }
)
}
/// .descending(\.property)
public static func descending<Property: Comparable>(
_ prop: KeyPath<Element, Property>
) -> SortOrder<Element>
{
return SortOrder(
isBefore: { $0[keyPath: prop] > $1[keyPath: prop] },
isEqual : { $0[keyPath: prop] == $1[keyPath: prop] }
)
}
}
// seq.sorted(_:)
extension Sequence {
/// ⭐️ items in a `Sequence` sorted by multiple properties.
/// example:
/// ```
/// items.sorted(.ascending(\.title))
/// items.sorted(.ascending(\.price), .descending(\.rate))
/// ```
public func sorted(_ orders: SortOrder<Element>...) -> [Element] {
return sorted { a, b in
// ⭐️ if some property is not equal, return the order
for order in orders {
guard order.isEqual(a, b)
else { return order.isBefore(a, b) }
}
// ⭐️ all properties are equal, so `a` is NOT before `b`
return false
}
}
}
Last updated
Was this helpful?