Skip to content

Instantly share code, notes, and snippets.

@karwa
Created February 22, 2024 15:53
Show Gist options
  • Save karwa/f5be3d665bc3f2d5e72879ef3759a038 to your computer and use it in GitHub Desktop.
Save karwa/f5be3d665bc3f2d5e72879ef3759a038 to your computer and use it in GitHub Desktop.
Varadic property comparison in Swift
public enum SortOrder {
case ascending
case descending
}
@inlinable @inline(__always)
public func isLessThan<Root, each Value: Comparable>(
_ leftRoot: Root,
_ rightRoot: Root,
comparing properties: repeat ((Root) -> each Value, SortOrder)
) -> Bool {
// This should get constant-folded.
var numProperties = 0
for _ in repeat each properties {
numProperties += 1
}
var propertyIdx = 0
for (getProperty, mode) in repeat each properties {
let (left, right) = (getProperty(leftRoot), getProperty(rightRoot))
// We can save an equality check for the final element.
if propertyIdx < numProperties {
guard left != right else {
propertyIdx += 1
continue
}
}
switch mode {
case .ascending: return left < right
case .descending: return left > right
}
}
return false
}
// --------------------------------
// Example Usage
// --------------------------------
extension Bool: @retroactive Comparable {
@inlinable @inline(__always)
public static func < (lhs: Self, rhs: Self) -> Bool {
(lhs ? 1 : 0) < (rhs ? 1 : 0)
}
}
struct Account: Comparable {
var name: String
var isActive: Bool
static func < (lhs: Self, rhs: Self) -> Bool {
isLessThan(
lhs, rhs,
comparing: (\.isActive, .descending), (\.name, .ascending)
)
}
}
let accounts = [
Account(name:"Albert", isActive: false),
Account(name:"Alice", isActive: true),
Account(name:"Becka", isActive: false),
Account(name:"Bob", isActive: true)
]
for a in accounts.sorted() {
print(a)
}
// Prints:
// Account(name: "Alice", isActive: true)
// Account(name: "Bob", isActive: true)
// Account(name: "Albert", isActive: false)
// Account(name: "Becka", isActive: false)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment