dictionary enums swift hashable

dictionary - ¿Cómo puedo usar Swift enum como una clave de diccionario?(Conforme a Equatable)



enums hashable (4)

Información sobre las enumeraciones como claves del diccionario:

Del libro de Swift:

Los valores de los miembros de la enumeración sin valores asociados (como se describe en Enumeraciones) también se pueden cargar de forma predeterminada.

Sin embargo, su enumeración tiene un valor de miembro con un valor asociado, por lo que Hashable debe agregar manualmente la conformidad de Hashable .

Solución

El problema con su implementación es que las declaraciones del operador en Swift deben tener un alcance global.

Sólo muévete:

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

fuera de la definición de enum y funcionará.

Consulte los documentos para obtener más información al respecto.

Definí una enumeración para representar una selección de una "estación"; las estaciones están definidas por un único entero positivo, así que he creado la siguiente enumeración para permitir que los valores negativos representen selecciones especiales:

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)" } } } }

Me gustaría usar estos valores como claves en un diccionario. Declarar un diccionario produce el error esperado de que StationSelector no se ajusta a Hashable. Ajustarse a Hashable es fácil con una simple función hash:

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

Sin embargo, Hashable requiere conformidad con Equatable , y no puedo definir el operador equals en mi enumeración para satisfacer al compilador.

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

El compilador se queja de que son dos declaraciones en una sola línea y quiere poner una ; después de la func , que tampoco tiene sentido.

¿Alguna idea?


Hashable un poco tratando de hacer una enum con valores asociados conforme a Hashable .

Aquí hice mi enum con valores asociados que se ajustan a Hashable para que pueda ser ordenada o utilizada como una clave de Dictionary , o hacer cualquier otra cosa que Hashable pueda hacer.

Hashable hacer que los valores asociados enum ajusten a Hashable porque las enums valores asociados no pueden tener un tipo sin enums .

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 } } }

También es necesario anular el operador de igualdad:

/// Override equality operator so Components Enum conforms to Hashable public func == (lhs: Components, rhs: Components) -> Bool { return lhs.toInt() == rhs.toInt() }


Solo por enfatizar lo que dijo Cezar antes. Si puede evitar tener una variable miembro, no necesita implementar el operador igual para hacer que las enums sean transferibles, ¡simplemente deles un tipo!

enum StationSelector : Int { case Nearest = 1, LastShown, List, Specific // automatically assigned to 1, 2, 3, 4 }

Eso es todo lo que necesitas. Ahora también puede iniciarlos con rawValue o recuperarlos más tarde.

let a: StationSelector? = StationSelector(rawValue: 2) // LastShown let b: StationSelector = .LastShown if(a == b) { print("Selectors are equal with value /(a?.rawValue)") }

Para más información, consulte la documentación .


Para una mayor legibilidad, reemplace StationSelector con Swift 3:

enum StationSelector { case nearest, lastShown, list, specific(Int) } extension StationSelector: RawRepresentable { typealias RawValue = Int init?(rawValue: RawValue) { switch rawValue { case -1: self = .nearest case -2: self = .lastShown case -3: self = .list case (let value) where value >= 0: self = .specific(value) default: return nil } } var rawValue: RawValue { switch self { case .nearest: return -1 case .lastShown: return -2 case .list: return -3 case .specific(let value) where value >= 0: return value default: fatalError("StationSelector is not valid") } } }

La API de desarrolladores de Apple dice sobre el protocolo Hashable :

Cuando define una enumeración sin valores asociados, gana la conformidad de Hashable automáticamente y puede agregar la conformidad de Hashable a sus otros tipos personalizados agregando una única propiedad de hashValue .

Por lo tanto, dado que StationSelector implementa los valores asociados, debe hacer que StationSelector ajuste al protocolo Hashable manualmente.

El primer paso es implementar == operator y hacer que StationSelector ajuste al protocolo Equatable :

extension StationSelector: Equatable { static func == (lhs: StationSelector, rhs: StationSelector) -> Bool { return lhs.rawValue == rhs.rawValue } }

Uso:

let nearest = StationSelector.nearest let lastShown = StationSelector.lastShown let specific0 = StationSelector.specific(0) // Requires == operator print(nearest == lastShown) // prints false print(nearest == specific0) // prints false // Requires Equatable protocol conformance let array = [nearest, lastShown, specific0] print(array.contains(nearest)) // prints true

Una vez Equatable protocolo Equatable , puede hacer que StationSelector ajuste al protocolo Hashable :

extension StationSelector: Hashable { var hashValue: Int { return self.rawValue.hashValue } }

Uso:

// Requires Hashable protocol conformance let dictionnary = [StationSelector.nearest: 5, StationSelector.lastShown: 10]

El siguiente código muestra la implementación requerida para que StationSelector haga conforme al protocolo Hashable usando Swift 3:

enum StationSelector: RawRepresentable, Hashable { case nearest, lastShown, list, specific(Int) typealias RawValue = Int init?(rawValue: RawValue) { switch rawValue { case -1: self = .nearest case -2: self = .lastShown case -3: self = .list case (let value) where value >= 0: self = .specific(value) default: return nil } } var rawValue: RawValue { switch self { case .nearest: return -1 case .lastShown: return -2 case .list: return -3 case .specific(let value) where value >= 0: return value default: fatalError("StationSelector is not valid") } } static func == (lhs: StationSelector, rhs: StationSelector) -> Bool { return lhs.rawValue == rhs.rawValue } var hashValue: Int { return self.rawValue.hashValue } }