seq.sorted(_:)
sort a sequence on multiple properties
ๆไบไธ้ข็ๆดๅ ๅพ๏ผๆๅๅฐฑๅฏไปฅ็จ้้บผๆผไบฎ็่ชๆณไพๅฐ็ฉไปถๆๅบ๏ผ
let sorted = items.sorted(
.descending(\.price),
.descending(\.rate),
.ascending (\.title)
)
ๅ็จ guard ... else ๅฏไปฅ่ฎใ็จๅคๅๅฑฌๆงไพๆๅบใ็่ชๆณ่ฎๅพๆด็ฐกๆฝใ
๐ 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
}
}
}
// Using extension
struct Post {
let title: String
let views: Int
let duration: Int
}
var posts = [
Post(title: "Alice", views: 1, duration: 3),
Post(title: "Beth" , views: 1, duration: 1),
Post(title: "Cloe" , views: 1, duration: 2),
Post(title: "David", views: 5, duration: 2),
Post(title: "Ethan", views: 4, duration: 10),
Post(title: "Felix", views: 5, duration: 2)
]
// โญ๏ธ seq.sorted(_:)
let sorted = posts.sorted(
.descending(\.views),
.descending(\.duration),
.ascending (\.title)
)
// [
// Post(title: "David", views: 5, duration: 2),
// Post(title: "Felix", views: 5, duration: 2),
// Post(title: "Ethan", views: 4, duration: 10),
// Post(title: "Alice", views: 1, duration: 3),
// Post(title: "Beth" , views: 1, duration: 2),
// Post(title: "Cloe" , views: 1, duration: 1)
// ]
๐ replit
// Without using extension
struct Post {
let title: String
let views: Int
let duration: Int
}
var posts = [
Post(title: "Alice", views: 1, duration: 3),
Post(title: "Beth", views: 1, duration: 2),
Post(title: "Cloe", views: 1, duration: 1),
Post(title: "David", views: 5, duration: 2),
Post(title: "Ethan", views: 4, duration: 10),
Post(title: "Felix", views: 5, duration: 2)
]
// ------------ main ------------
// sort in place
posts.sort { // $0 is before $1 ?
// check on `views` first
guard $0.views == $1.views else {
return $0.views > $1.views // decreasing
}
// $0.views == $1.views
// check on `duration` next
guard $0.duration == $1.duration else {
return $0.duration > $1.duration // decreasing
}
// $0.views == $1.views
// $0.duration == $1.duration
// check on `title` last
return $0.title < $1.title // increasing
}
print(posts)
// [
// Post(title: "David", views: 5, duration: 2),
// Post(title: "Felix", views: 5, duration: 2),
// Post(title: "Ethan", views: 4, duration: 10),
// Post(title: "Alice", views: 1, duration: 3),
// Post(title: "Beth" , views: 1, duration: 2),
// Post(title: "Cloe" , views: 1, duration: 1)
// ]
Web Dev Nโฉotes โฉ sort by multiple keys
KeyPathKit โฉ .sort(by:) , source code โญ๏ธโญ๏ธโญ๏ธ
contacts.sorted(by: .ascending(\.lastName), .descending(\.age))
Last updated