Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use a Swift enum as a Dictionary key? (Conforming to Equatable)

I've defined an enum to represent a selection of a "station"; stations are defined by a unique positive integer, so I've created the following enum to allow negative values to represent special selections:

enum StationSelector : Printable {     case Nearest     case LastShown     case List     case Specific(Int)      func toInt() -> Int {         switch self {         case .Nearest:             return -1         case .LastShown:             return -2         case .List:             return -3         case .Specific(let stationNum):             return stationNum         }     }      static func fromInt(value:Int) -> StationSelector? {         if value > 0 {             return StationSelector.Specific(value)         }         switch value {         case -1:             return StationSelector.Nearest         case -2:             return StationSelector.LastShown         case -3:             return StationSelector.List         default:             return nil         }     }      var description: String {     get {         switch self {         case .Nearest:             return "Nearest Station"         case .LastShown:             return "Last Displayed Station"         case .List:             return "Station List"         case .Specific(let stationNumber):             return "Station #\(stationNumber)"         }     }     } } 

I'd like to use these values as keys in a dictionary. Declaring a Dictionary yields the expected error that StationSelector doesn't conform to Hashable. Conforming to Hashable is easy with a simple hash function:

var hashValue: Int { get {     return self.toInt() } } 

However, Hashable requires conformance to Equatable, and I can't seem to define the equals operator on my enum to satisfy the compiler.

func == (lhs: StationSelector, rhs: StationSelector) -> Bool {     return lhs.toInt() == rhs.toInt() } 

The compiler complains that this is two declarations on a single line and wants to put a ; after func, which doesn't make sense, either.

Any thoughts?

like image 395
Doug Knowles Avatar asked Jul 03 '14 19:07

Doug Knowles


People also ask

Can enum conform to Swift protocol?

Yes, enums can conform protocols. You can use Swift's own protocols or custom protocols. By using protocols with Enums you can add more capabilities.

Is enum Equatable?

An enum without associated values has Equatable conformance even without the declaration. For an enum , all its associated values must conform to Equatable .

Is enum hashable?

For a struct , all its stored properties must conform to Hashable . For an enum , all its associated values must conform to Hashable . (An enum without associated values has Hashable conformance even without the declaration.)

Can a Swift enum have functions?

You cannot make a function only available to one case of an enum. Any function in an enum is available to every case of the enum.


2 Answers

Info on Enumerations as dictionary keys:

From the Swift book:

Enumeration member values without associated values (as described in Enumerations) are also hashable by default.

However, your Enumeration does have a member value with an associated value, so Hashable conformance has to be added manually by you.

Solution

The problem with your implementation, is that operator declarations in Swift must be at a global scope.

Just move:

func == (lhs: StationSelector, rhs: StationSelector) -> Bool {     return lhs.toInt() == rhs.toInt() } 

outside the enum definition and it will work.

Check the docs for more on that.

like image 183
Cezar Avatar answered Sep 28 '22 09:09

Cezar


I struggled for a little trying to make an enum with associated values conform to Hashable.

Here's I made my enum with associated values conform to Hashable so it could be sorted or used as a Dictionary key, or do anything else that Hashable can do.

You have to make your associated values enum conform to Hashable because associated values enums cannot have a raw type.

public enum Components: Hashable {     case None     case Year(Int?)     case Month(Int?)     case Week(Int?)     case Day(Int?)     case Hour(Int?)     case Minute(Int?)     case Second(Int?)      ///The hashValue of the `Component` so we can conform to `Hashable` and be sorted.     public var hashValue : Int {         return self.toInt()     }      /// Return an 'Int' value for each `Component` type so `Component` can conform to `Hashable`     private func toInt() -> Int {         switch self {         case .None:             return -1         case .Year:             return 0         case .Month:             return 1         case .Week:             return 2         case .Day:             return 3         case .Hour:             return 4         case .Minute:             return 5         case .Second:             return 6         }      }  } 

Also need to override the equality operator:

/// Override equality operator so Components Enum conforms to Hashable public func == (lhs: Components, rhs: Components) -> Bool {     return lhs.toInt() == rhs.toInt() } 
like image 45
Sakiboy Avatar answered Sep 28 '22 09:09

Sakiboy