Created
February 22, 2024 15:53
-
-
Save karwa/f5be3d665bc3f2d5e72879ef3759a038 to your computer and use it in GitHub Desktop.
Varadic property comparison in Swift
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
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