Skip to content

Instantly share code, notes, and snippets.

@CrystDragon
Created June 20, 2019 09:33
Show Gist options
  • Save CrystDragon/01832271cb6ba80d807c66a748f529b0 to your computer and use it in GitHub Desktop.
Save CrystDragon/01832271cb6ba80d807c66a748f529b0 to your computer and use it in GitHub Desktop.
A type-erased keyPath-based multi-argument max helper
/// 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