Tools for checking whether two values are "close" to equal in Swift.
This package offers small, focused APIs for a few common "near equality" paradigms:
- Unordered collection equality (compare collections ignoring order)
- Value proximity checks via
AlmostEquatable. - Selective equality via
SelectiveEquatable(check only specific key paths) - "equivalence" checking (experimental).
Many comparisons don't require strict, bit-for-bit equality. This package provides helpers to express common "close enough" equality checks in a clear, testable way.
Key concepts
hasSameElements(as:)— compare two collections for equality while ignoring element order.AlmostEquatable— a protocol for types that can be compared within a threshold (tolerance).SelectiveEquatable— a protocol (from the SelectiveEquatable package) where equality is performed only on the provided key paths.- equivalence (experimental):
SelectiveEquatablepackage vends a set ofisEquivalent(to:). These compare two collections, ignore order, and verify that each collection has exactly one element with a matching id and other values.
Add the package to your Package.swift dependencies:
.package(url: "https://github.com/yourusername/KindaSortaEqual.git", from: "0.1.0"),Then add the product to your target dependencies.
Below are short examples for the main paradigms. They assume the helpers are exported from the KindaSortaEqual module.
Use hasSameElements(as:) to check two collections contain the same elements regardless of order. This is useful when element order is not meaningful.
import KindaSortaEqual
let a = [1, 2, 3]
let b = [3, 1, 2]
let c = [1, 2, 3, 3]
a.hasSameElements(as: b) // true
a.hasSameElements(as: c) // falseTo count as true, both collections must contain the same elements with the same multiplicity. The order of elements does not matter.
AlmostEquatable is a lightweight protocol used to express that two values are "close" by some measure (for example, within a tolerance for floating point values). Types conforming to AlmostEquatable implement a method such as isAlmostEqual(to:threshold:).
import KindaSortaEqual
extension Double: AlmostEquatable {
func isAlmostEqual(to other: Double, threshold: Double = 1e-8) -> Bool {
return abs(self - other) <= threshold
}
}
let x: Double = 0.30000000000000004
let y: Double = 0.3
print(x.isAlmostEqual(to: y, threshold: 1e-12))The package includes conformances and helpers for arrays, floating point types, CoreGraphics types, and some common custom types—see the Sources/KindaSortaEqual/AlmostEquatable folder for concrete implementations and tests.
The library comes with several built-in conformances for common types including:
Double,FloatCGPoint,CGSize,CGRectDateArrayUIColor,NSColor
SelectiveEquatable (from the SelectiveEquatable package) lets you state equality in terms of a set of key paths. Provide the key paths you care about and equality will be checked only for those properties.
Refer to the Sources/KindaSortaEqual/SelectiveEquatable.swift for the exact API surface used by this package.
Conforming to SelectiveEquatable is as simple as:
extension MyType: SelectiveEquatable {}That's it. No methods need to be implemented. If you have any equatable properties then you can instantly benefit from SelectiveEquatable.
let person1 = Person(name: "Alice", age: 30, address: "123 Main St")
let person2 = Person(name: "Alice", age: 30, address: "456 Elm St")
person1.isEqual(to: person2, by: \.name, \.age) // trueBe sure to check out Uncertain by Mattt which has some similar ideas but is solving a fundamentally different problem.
Contributions are welcome. Please open issues or PRs. Keep changes small and focused. Add tests for new behavior.
Licensed by the MIT License.
This repository includes a LICENSE file—please refer to it for licensing information.