How to use custom class type as key dictionary in Swift dictionaries?

I am working on a project created using Swift, and I am trying to create a dictionary for storing objects of a custom Pixel class (KEY, for storing color information such as RGB values) and int values ​​(value for counting how many times the same color appears on one image).

If it is in C #, the working code should be:

Dictionary<Pixel, int> colorDictionary = new Dictionary< Pixel, int> () ;

In Swift, I tried:

 var colorDictionary = Dictionary<Pixel, Int>() 

However, the error I received:

"Pixel type does not conform to hashable protocol"

What should I do to solve this problem? Thank you very much!

+10
source share
4 answers

Any custom type that you want to use a dictionary key must comply with the Hashable protocol.

This protocol has one property that you must implement.

var hashValue: Int { get }

Use this property to generate an int that the dictionary can use to search. You should try to make the generated hashValue unique to each pixel.

There is the following note in the Swift book, so you can make a random hash (as long as it is unique):

The value returned by a property of type hashValue does not have to be the same for different executions of the same program or in different programs.

Note that since Hashable inherits from Equatable , you must also implement:

 func ==(_ lhs: Self, _ rhs: Self) -> Bool. 

I'm not sure what the internal structure of your pixel is, but you can probably consider two pixels equal if both have the same x and y values. The final logic is up to you.

Change this as you need:

 struct Pixel : Hashable { // MARK: Hashable var hashValue: Int { get { // Do some operations to generate a unique hash. } } } //MARK: Equatable func ==(lh: Pixel, rh: Pixel) -> Bool { return lh.x == rh.x && rh.y == lh.y } 
+4
source

Continuing the story of Andy Ibanes. The shortcut to implementing hashValue is to compose String hashValue. You could do something like this.

 class Pixel: Hashable { var r:Int = 0; var g:Int = 0; var b:Int = 0; var a:Int = 0; var hashValue: Int { get { return "\(r)\(g)\(b)\(a)".hashValue; } } } 

You also need the Equatable function because hashValues ​​in this case is just a quick check to check for two objects that are not equal. Since for two objects it can have the same hashValue, but not be equal, you still need to implement == to determine equality, as shown below.

 func ==(lhs: Pixel, rhs: Pixel) -> Bool{ if lhs.r != rhs.r{ return false; } if lhs.g != rhs.g{ return false; } if lhs.b != rhs.b{ return false; } if lhs.a != rhs.a{ return false; } return true; } 
+4
source

Implement the Hashable protocol as follows:

 class Pixel : Hashable { var alpha, red, green, blue : Int init(red: Int, green: Int, blue: Int, alpha: Int) { self.red = red self.green = green self.blue = blue self.alpha = alpha } var hashValue : Int { get { return alpha ^ red ^ green ^ blue } } } func ==(lhs: Pixel, rhs: Pixel) -> Bool { return lhs.alpha == rhs.alpha && lhs.red == rhs.red && lhs.green == rhs.green && lhs.blue == rhs.blue } 
+1
source

Starting with swift 4.2, hashValue deprecated as a Hashable requirement.

Now, if you want to customize how your type implements Hashable , you can override the hash(into:) method instead of hashValue. The hash(into:) method passes the Hasher object to the link that you call combine(_:) to add the necessary information about the state of your type.

 class Pixel { var alpha, red, green, blue : Int } // Hashable implementation extension Pixel : Hashable { func hash(into hasher: inout Hasher) { hasher.combine(self.red) hasher.combine(self.green) hasher.combine(self.blue) hasher.combine(self.alpha) } } 
+1
source

All Articles