Created
June 20, 2019 09:33
-
-
Save CrystDragon/01832271cb6ba80d807c66a748f529b0 to your computer and use it in GitHub Desktop.
A type-erased keyPath-based multi-argument max helper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// library | |
/// a type-erased interface | |
private final class ComparatorHolder<S: Sequence> { | |
private typealias Comparator = (S.Element, S.Element) -> Int | |
private let base: S | |
private var comparators: [Comparator] = [] | |
init(_ base: S) { | |
self.base = base | |
} | |
func vend<T: Comparable>(_ keyPath: KeyPath<S.Element, T>) -> Self { | |
comparators.append { (lhs, rhs) -> Int in | |
let element1 = lhs[keyPath: keyPath] | |
let element2 = rhs[keyPath: keyPath] | |
if element1 < element2 { | |
return -1 | |
} else if element1 > element2 { | |
return 1 | |
} else { | |
return 0 | |
} | |
} | |
return self | |
} | |
private func totalCompare(lhs: S.Element, rhs: S.Element) -> Bool { | |
for comparator in comparators { | |
let partialResult = comparator(lhs, rhs) | |
if partialResult == 0 { | |
continue | |
} | |
return partialResult < 0 | |
} | |
return false | |
} | |
func findMax() -> S.Element? { | |
return base.max(by: self.totalCompare) | |
} | |
} | |
extension Sequence { | |
func max<T1: Comparable>(keyPath: KeyPath<Element, T1>) -> Element? { | |
let holder = ComparatorHolder(self).vend(keyPath) | |
return holder.findMax() | |
} | |
func max< | |
T1: Comparable, | |
T2: Comparable | |
>( | |
keyPaths k1: KeyPath<Element, T1>, | |
_ k2: KeyPath<Element, T2> | |
) -> Element? { | |
let holder = ComparatorHolder(self).vend(k1).vend(k2) | |
return holder.findMax() | |
} | |
func max< | |
T1: Comparable, | |
T2: Comparable, | |
T3: Comparable | |
>( | |
keyPaths k1: KeyPath<Element, T1>, | |
_ k2: KeyPath<Element, T2>, | |
_ k3: KeyPath<Element, T3> | |
) -> Element? { | |
let holder = ComparatorHolder(self).vend(k1).vend(k2).vend(k3) | |
return holder.findMax() | |
} | |
func max< | |
T1: Comparable, | |
T2: Comparable, | |
T3: Comparable, | |
T4: Comparable | |
>( | |
keyPaths k1: KeyPath<Element, T1>, | |
_ k2: KeyPath<Element, T2>, | |
_ k3: KeyPath<Element, T3>, | |
_ k4: KeyPath<Element, T4> | |
) -> Element? { | |
let holder = ComparatorHolder(self).vend(k1).vend(k2).vend(k3).vend(k4) | |
return holder.findMax() | |
} | |
} | |
// user | |
let indexPaths = [ | |
IndexPath(item: 0, section: 0), | |
IndexPath(item: 1, section: 1), | |
IndexPath(item: 1, section: 0), | |
IndexPath(item: 0, section: 1), | |
] | |
let maxIndexPath = indexPaths.max(keyPaths: \.section, \.item) | |
struct People { | |
let name: String | |
let age: Int | |
} | |
let peoples = [ | |
People(name: "a", age: 10), | |
People(name: "b", age: 10), | |
People(name: "a", age: 13), | |
] | |
let maxPeople = peoples.max(keyPaths: \.age, \.name) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment