Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use a custom class type to be the key in Dictionary in Swift?

I am working on a project built by using Swift, and I am trying to create a dictionary to store objects of a custom class called Pixel(KEY, for storing colour information such as RGB values) and int values (Value, for counting how many times the same colour appearing on the same image).

If this 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 got:

"Type 'Pixel' does not conform to protocol 'Hashable'"

What should I do to solve this? Thanks very much!

like image 860
Ares Li Avatar asked Jul 10 '15 04:07

Ares Li


People also ask

Is it possible to use a custom struct as a dictionary key in Swift?

Any type that conforms to the Hashable protocol can be used as a dictionary's Key type, including all of Swift's basic types. You can use your own custom types as dictionary keys by making them conform to the Hashable protocol.

Which method is used to update value for a particular key in a dictionary in Swift?

Modifying Key-Value Pairs To change the value of a key-value pair, use the . updateValue() method or subscript syntax by appending brackets [ ] with an existing key inside them to a dictionary's name and then adding an assignment operator ( = ) followed by the modified value.

How do you initiate a dictionary in Swift?

Use explicit variable typing, or let Swift infer the type of the variable based on the value assigned to it. Use the formal specified generic struct notation Dictionary<String,Double> , or use the built-in "syntactic sugar" for describing a dictionary type [String:Double] .

Why is dictionary unordered in Swift?

Sets are unordered collections of unique values. Dictionaries are unordered collections of key-value associations. Arrays, sets, and dictionaries in Swift are always clear about the types of values and keys that they can store. This means that you can't insert a value of the wrong type into a collection by mistake.


2 Answers

From swift 4.2, hashValue is 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 a Hasher object by reference, which you call combine(_:) on to add the essential state information 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)
    }
}
like image 61
Surjeet Rajput Avatar answered Oct 24 '22 23:10

Surjeet Rajput


Continuing on with what Andy Ibanez posted. A shortcut to implementing a hashValue is to piggyback off String's 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 an Equatable function because hashValues in this case are merely a quick check for verifying two objects are not equal. Since it is possible for two objects to have the same hashValue, but not be equal, you still need to implement == to define equality like 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;
}
like image 35
Rich Fox Avatar answered Oct 24 '22 21:10

Rich Fox