Skip to content

Instantly share code, notes, and snippets.

@timvermeulen
Last active October 15, 2018 02:35
Show Gist options
  • Save timvermeulen/46c58d28f5dc37ac3406280f2a0486e5 to your computer and use it in GitHub Desktop.
Save timvermeulen/46c58d28f5dc37ac3406280f2a0486e5 to your computer and use it in GitHub Desktop.
// Monoid
precedencegroup MonoidPrecedence {
associativity: left
}
infix operator <> : MonoidPrecedence
protocol Monoid {
static var empty: Self { get }
static func <> (lhs: Self, rhs: Self) -> Self
}
// SortDescriptor
struct SortDescriptor<Value> {
let isOrderedBefore: (Value, Value) -> Bool
}
prefix operator ~
extension SortDescriptor {
init<T>(_ block: @escaping (Value) -> T, _ isOrderedBefore: @escaping (T, T) -> Bool) {
self.init { isOrderedBefore(block($1), block($0)) }
}
init<T: Comparable>(_ block: @escaping (Value) -> T) {
self.init(block, <)
}
static prefix func ~ (value: SortDescriptor) -> SortDescriptor {
return .init { value.isOrderedBefore($1, $0) }
}
}
extension SortDescriptor: Monoid {
static var empty: SortDescriptor<Value> {
return SortDescriptor { _, _ in false }
}
static func <> (lhs: SortDescriptor<Value>, rhs: SortDescriptor<Value>) -> SortDescriptor<Value> {
return SortDescriptor { lhs.isOrderedBefore($0, $1) ? true : lhs.isOrderedBefore($1, $0) ? false : rhs.isOrderedBefore($0, $1) }
}
}
extension Sequence {
func sorted(by descriptor: SortDescriptor<Element>) -> [Element] {
return sorted(by: descriptor.isOrderedBefore)
}
}
// KeyPaths
prefix operator ^
prefix func ^ <Value, T: Comparable> (keyPath: KeyPath<Value, T>) -> SortDescriptor<Value> {
return .init(^keyPath)
}
extension KeyPath {
static prefix func ^ (keyPath: KeyPath) -> (Root) -> Value {
return { $0[keyPath: keyPath] }
}
}
// Example
import Foundation
struct Person {
let first: String
let last: String
let yearOfBirth: Int
}
let people = [
Person(first: "Jo", last: "Smith", yearOfBirth: 1970),
Person(first: "Joanne", last: "Williams", yearOfBirth: 1985),
Person(first: "Annie", last: "Williams", yearOfBirth: 1985),
Person(first: "Robert", last: "Jones", yearOfBirth: 1990)
]
let firstName = SortDescriptor<Person> { $0.first }
let lastName = SortDescriptor(^\Person.last) { $0.localizedCaseInsensitiveCompare($1) == .orderedAscending }
let sorted = people.sorted(by: ^\.yearOfBirth <> firstName <> ~lastName)
print(sorted.map(^\.first)) // ["Robert", "Joanne", "Annie", "Jo"]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment