I'd like to use a very simple tuple as a key:
(Int, Int)
Dictionary keys need to be Hashable. I've learnt.
But can't find how I make this simple tuple Hashable, and do struggle with protocol conformance at the best of times.
More profoundly, a CGPoint would solve my problems. It can be of this format, but is not hashable.
Is it possible to extend a CGPoint so it's hashable? If so, how?
EDIT: Image of Int variant of CGPoint choice.
Making conform to Hashable
is not difficult for class, struct or enum.
You just need to explicitly declare conformance to Hashable
and define a property hashValue: Int
. Practically, the hashValue
needs to fulfill one simple axiom: if a == b then a.hashValue == b.hashValue.
(To conform Hashable
, you also need to make the type Equatable
. In case of CGPoint
, it is already Equatable
.)
An example to make CGPoint
conform to Hashable
:
extension CGPoint: Hashable {
public var hashValue: Int {
//This expression can be any of the arbitrary expression which fulfills the axiom above.
return x.hashValue ^ y.hashValue
}
}
var pointDict: [CGPoint: String] = [
CGPoint(x: 1.0, y: 2.0): "PointA",
CGPoint(x: 3.0, y: 4.0): "PointB",
CGPoint(x: 5.0, y: 6.0): "PointC",
]
print(pointDict[CGPoint(x: 1.0, y: 2.0)]) //->Optional("PointA")
As CGPoint
contains CGFloat
values, so, CGPoint
as Key of Dictionary may cause unexpected behavior based on the calculation error of binary floating-point system. You need to use it with extra caution.
ADDITION
If you want to avoid some calculation error issue and can accept that the structure can only contain Int
s, you can define your own struct and make it conform to Hashable
:
struct MyPoint {
var x: Int
var y: Int
}
extension MyPoint: Hashable {
public var hashValue: Int {
return x.hashValue ^ y.hashValue
}
public static func == (lhs: MyPoint, rhs: MyPoint) -> Bool {
return lhs.x == rhs.x && lhs.y == rhs.y
}
}
var myPointDict: [MyPoint: String] = [
MyPoint(x: 1, y: 2): "MyPointA",
MyPoint(x: 3, y: 4): "MyPointB",
MyPoint(x: 5, y: 6): "MyPointC",
]
print(myPointDict[MyPoint(x: 1, y: 2)]) //->Optional("MyPointA")
Not much more difficult than the code above, one more thing you need is just defining ==
operator for the struct. Please make it a try.