with switch protocol parameter generic enum swift enums count

switch - swift4 enum



¿Cómo obtengo el recuento de una enumeración Swift? (25)

Enum de cadena con índice

enum eEventTabType : String { case Search = "SEARCH" case Inbox = "INBOX" case Accepted = "ACCEPTED" case Saved = "SAVED" case Declined = "DECLINED" case Organized = "ORGANIZED" static let allValues = [Search, Inbox, Accepted, Saved, Declined, Organized] var index : Int { return eEventTabType.allValues.indexOf(self)! } }

cuenta: eEventTabType.allValues.count

index: objeEventTabType.index

Disfruta :)

¿Cómo puedo determinar el número de casos en una enumeración Swift?

(Me gustaría evitar enumerar manualmente todos los valores , o usar el viejo " truco enum_count " si es posible).


¿Por qué lo haces todo tan complejo? El contador SIMPLEST de Int enum es agregar:

case Count

En el final. Y ... viola - ahora tienes la cuenta - rápido y simple


A partir de Swift 4.2 (Xcode 10) puede declarar conformidad con el protocolo CaseIterable , esto funciona para todas las enumeraciones sin valores asociados:

enum Stuff: CaseIterable { case first case second case third case forth }

El número de casos ahora se obtiene simplemente con

print(Stuff.allCases.count) // 4

Para más información, ver


Ampliando la respuesta de Matthieu Riegler, esta es una solución para Swift 3 que no requiere el uso de genéricos, y se puede llamar fácilmente usando el tipo enum con EnumType.elementsCount :

extension RawRepresentable where Self: Hashable { // Returns the number of elements in a RawRepresentable data structure static var elementsCount: Int { var i = 1 while (withUnsafePointer(to: &i, { return $0.withMemoryRebound(to: self, capacity: 1, { return $0.pointee }) }).hashValue != 0) { i += 1 } return i }


Cree una matriz estática de todos los valores como se muestra en esta answer

enum ProductCategory : String { case Washers = "washers", Dryers = "dryers", Toasters = "toasters" static let allValues = [Washers, Dryers, Toasters] } ... let count = ProductCategory.allValues.count

Esto también es útil cuando desea enumerar los valores, y funciona para todos los tipos de Enum


Defino un protocolo reutilizable que realiza automáticamente el recuento de casos según el enfoque publicado por Nate Cook.

protocol CaseCountable { static var caseCount: Int { get } } extension CaseCountable where Self: RawRepresentable, Self.RawValue == Int { internal static var caseCount: Int { var count = 0 while let _ = Self(rawValue: count) { count += 1 } return count } }

Entonces puedo reutilizar este protocolo, por ejemplo, de la siguiente manera:

enum Planet : Int, CaseCountable { case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune } //.. print(Planet.caseCount)


El siguiente método proviene de CoreKit y es similar a las respuestas que otros han sugerido. Esto funciona con Swift 4.

public protocol EnumCollection: Hashable { static func cases() -> AnySequence<Self> static var allValues: [Self] { get } } public extension EnumCollection { public static func cases() -> AnySequence<Self> { return AnySequence { () -> AnyIterator<Self> in var raw = 0 return AnyIterator { let current: Self = withUnsafePointer(to: &raw) { $0.withMemoryRebound(to: self, capacity: 1) { $0.pointee } } guard current.hashValue == raw else { return nil } raw += 1 return current } } } public static var allValues: [Self] { return Array(self.cases()) } }

enum Weekdays: String, EnumCollection { case sunday, monday, tuesday, wednesday, thursday, friday, saturday }

Entonces solo necesita llamar a Weekdays.allValues.count .


Escribí una extensión simple que le da a todas las enumeraciones donde el valor bruto es entero una propiedad de count :

extension RawRepresentable where RawValue: IntegerType { static var count: Int { var i: RawValue = 0 while let _ = Self(rawValue: i) { i = i.successor() } return Int(i.toIntMax()) } }

Desafortunadamente, le da la propiedad de count a OptionSetType donde no funcionará correctamente, así que aquí hay otra versión que requiere conformidad explícita con el protocolo CaseCountable para cualquier enumeración en los casos que desea contar:

protocol CaseCountable: RawRepresentable {} extension CaseCountable where RawValue: IntegerType { static var count: Int { var i: RawValue = 0 while let _ = Self(rawValue: i) { i = i.successor() } return Int(i.toIntMax()) } }

Es muy similar al enfoque publicado por Tom Pelaia, pero funciona con todos los tipos enteros.


Esta función se basa en 2 comportamientos de enum corriente no documentada (Swift 1.1):

  • El diseño de memoria de enum es solo un índice de case y case . Si el recuento de casos es de 2 a 256, es UInt8 .
  • Si la enum se enum bits a partir de un índice de caso no válido , su valor hashValue es 0

Así que úselo bajo su propio riesgo :)

func enumCaseCount<T:Hashable>(t:T.Type) -> Int { switch sizeof(t) { case 0: return 1 case 1: for i in 2..<256 { if unsafeBitCast(UInt8(i), t).hashValue == 0 { return i } } return 256 case 2: for i in 257..<65536 { if unsafeBitCast(UInt16(i), t).hashValue == 0 { return i } } return 65536 default: fatalError("too many") } }

Uso:

enum Foo:String { case C000 = "foo" case C001 = "bar" case C002 = "baz" } enumCaseCount(Foo) // -> 3


Este tipo de función puede devolver el recuento de su enumeración.

Swift 2 :

func enumCount<T: Hashable>(_: T.Type) -> Int { var i = 1 while (withUnsafePointer(&i) { UnsafePointer<T>($0).memory }).hashValue != 0 { i += 1 } return i }

Swift 3 :

func enumCount<T: Hashable>(_: T.Type) -> Int { var i = 1 while (withUnsafePointer(to: &i, { return $0.withMemoryRebound(to: T.self, capacity: 1, { return $0.pointee }) }).hashValue != 0) { i += 1 } return i }


Esto es menor, pero creo que una mejor solución O (1) sería la siguiente ( SOLO si su enumeración es Int comenzando en x, etc.):

enum Test : Int { case ONE = 1 case TWO case THREE case FOUR // if you later need to add additional enums add above COUNT so COUNT is always the last enum value case COUNT static var count: Int { return Test.COUNT.rawValue } // note if your enum starts at 0, some other number, etc. you''ll need to add on to the raw value the differential }

La respuesta seleccionada actual que sigo creyendo es la mejor respuesta para todas las enumeraciones, a menos que esté trabajando con Int , le recomiendo esta solución.


O simplemente puede definir _count fuera de la enumeración y adjuntarlo estáticamente:

let _count: Int = { var max: Int = 0 while let _ = EnumName(rawValue: max) { max += 1 } return max }() enum EnumName: Int { case val0 = 0 case val1 static let count = _count }

De esa manera, no importa cuántas enumeraciones cree, solo se creará una vez.

(elimine esta respuesta si static hace eso)


Oh, hola a todos, ¿qué pasa con las pruebas unitarias?

func testEnumCountIsEqualToNumberOfItemsInEnum() { var max: Int = 0 while let _ = Test(rawValue: max) { max += 1 } XCTAssert(max == Test.count) }

Esto combinado con la solución de Antonio:

enum Test { case one case two case three case four static var count: Int { return Test.four.hashValue + 1} }

en el código principal le da O (1) además de obtener una prueba fallida si alguien agrega un caso de enumeración five y no actualiza la implementación del count .


Para mi caso de uso, en una base de código donde varias personas podrían estar agregando claves a una enumeración, y estos casos deberían estar disponibles en la propiedad allKeys, es importante que todas las claves sean validadas contra las claves de la enumeración. Esto es para evitar que alguien olvide agregar su clave a la lista de todas las claves. Al hacer coincidir el recuento de la matriz allKeys (primero creado como un conjunto para evitar engaños) con el número de claves en la enumeración se garantiza que todos estén presentes.

Algunas de las respuestas anteriores muestran la forma de lograr esto en Swift 2, pero ninguna funciona en Swift 3 . Aquí está la versión formateada de Swift 3 :

static func enumCount<T: Hashable>(_ t: T.Type) -> Int { var i = 1 while (withUnsafePointer(to: &i) { $0.withMemoryRebound(to:t.self, capacity:1) { $0.pointee.hashValue != 0 } }) { i += 1 } return i } static var allKeys: [YourEnumTypeHere] { var enumSize = enumCount(YourEnumTypeHere.self) let keys: Set<YourEnumTypeHere> = [.all, .your, .cases, .here] guard keys.count == enumSize else { fatalError("Missmatch between allKeys(/(keys.count)) and actual keys(/(enumSize)) in enum.") } return Array(keys) }

Dependiendo de su caso de uso, es posible que desee ejecutar la prueba en desarrollo para evitar la sobrecarga de usar allKeys en cada solicitud


Por supuesto, no es dinámico, pero para muchos usos puede obtener una var estática agregada a su Enum

static var count: Int{ return 7 }

y luego EnumName.count como EnumName.count


Puede usar una constante estática que contiene el último valor de la enumeración más uno.

enum Color : Int { case Red, Orange, Yellow, Green, Cyan, Blue, Purple static let count: Int = Color.Purple.rawValue + 1 func toUIColor() -> UIColor{ switch self { case .Red: return UIColor.redColor() case .Orange: return UIColor.orangeColor() case .Yellow: return UIColor.yellowColor() case .Green: return UIColor.greenColor() case .Cyan: return UIColor.cyanColor() case .Blue: return UIColor.blueColor() case .Purple: return UIColor.redColor() } } }


Resolví este problema por mí mismo creando un protocolo (EnumIntArray) y una función de utilidad global (enumIntArray) que hacen que sea muy fácil agregar una variable "All" a cualquier enumeración (usando swift 1.2). La variable "all" contendrá una matriz de todos los elementos en la enumeración para que pueda usar all.count para el conteo

Solo funciona con enumeraciones que usan valores sin formato del tipo Int, pero quizás puede proporcionar alguna inspiración para otros tipos.

También aborda los problemas de "brecha en la numeración" y "tiempo excesivo para repetir" que he leído anteriormente y en otros lugares.

La idea es agregar el protocolo EnumIntArray a su enumeración y luego definir una variable estática "all" llamando a la función enumIntArray y proporcionarle el primer elemento (y el último si hay espacios en la numeración).

Debido a que la variable estática solo se inicializa una vez, la sobrecarga de pasar por todos los valores sin procesar solo afecta a su programa una vez.

ejemplo (sin espacios):

enum Animals:Int, EnumIntArray { case Cat=1, Dog, Rabbit, Chicken, Cow static var all = enumIntArray(Animals.Cat) }

ejemplo (con huecos):

enum Animals:Int, EnumIntArray { case Cat = 1, Dog, case Rabbit = 10, Chicken, Cow static var all = enumIntArray(Animals.Cat, Animals.Cow) }

Aquí está el código que lo implementa:

protocol EnumIntArray { init?(rawValue:Int) var rawValue:Int { get } } func enumIntArray<T:EnumIntArray>(firstValue:T, _ lastValue:T? = nil) -> [T] { var result:[T] = [] var rawValue = firstValue.rawValue while true { if let enumValue = T(rawValue:rawValue++) { result.append(enumValue) } else if lastValue == nil { break } if lastValue != nil && rawValue > lastValue!.rawValue { break } } return result }


Si la implementación no tiene nada en contra del uso de enumeraciones enteras, puede agregar un valor de miembro adicional llamado Count para representar el número de miembros en la enumeración; consulte el siguiente ejemplo:

enum TableViewSections : Int { case Watchlist case AddButton case Count }

Ahora puede obtener el número de miembros en la enumeración llamando a TableViewSections.Count.rawValue , que devolverá 2 para el ejemplo anterior.

Cuando maneja la enumeración en una instrucción de cambio, asegúrese de lanzar un error de aserción cuando encuentre al miembro Count donde no lo espera:

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { let currentSection: TableViewSections = TableViewSections.init(rawValue:section)! switch(currentSection) { case .Watchlist: return watchlist.count case .AddButton: return 1 case .Count: assert(false, "Invalid table view section!") } }


Si no desea basar su código en la última enumeración, puede crear esta función dentro de su enumeración.

func getNumberOfItems() -> Int { var i:Int = 0 var exit:Bool = false while !exit { if let menuIndex = MenuIndex(rawValue: i) { i++ }else{ exit = true } } return i }


Tengo una publicación de blog que entra en más detalles sobre esto, pero siempre que el tipo sin formato de su enumeración sea un entero, puede agregar un recuento de esta manera:

enum Reindeer: Int { case Dasher, Dancer, Prancer, Vixen, Comet, Cupid, Donner, Blitzen case Rudolph static let count: Int = { var max: Int = 0 while let _ = Reindeer(rawValue: max) { max += 1 } return max }() }


Una versión de Swift 3 que funciona con enumeraciones de tipo Int :

protocol CaseCountable: RawRepresentable {} extension CaseCountable where RawValue == Int { static var count: RawValue { var i: RawValue = 0 while let _ = Self(rawValue: i) { i += 1 } return i } }

Créditos: Basado en las respuestas de bzz y Nate Cook.

Generic IntegerType (en Swift 3 renombrado a Integer ) no es compatible, ya que es un tipo genérico muy fragmentado que carece de muchas funciones. successor ya no está disponible con Swift 3.

Tenga en cuenta que el comentario de Code Commander a la respuesta de Nate Cooks sigue siendo válido:

Si bien es bueno porque no necesita codificar un valor, esto creará una instancia de cada valor de enumeración cada vez que se llame. Eso es O (n) en lugar de O (1).

Hasta donde sé, actualmente no hay una solución alternativa cuando se usa esto como extensión de protocolo (y no se implementa en cada enumeración como lo hizo Nate Cook) debido a que las propiedades estáticas almacenadas no son compatibles con los tipos genéricos.

De todos modos, para enumeraciones pequeñas esto no debería ser un problema. Un caso de uso típico sería la UITableViews para UITableViews como ya mencionó Zorayr.


Actualización de Xcode 10

Adopte el protocolo CaseIterable en la enumeración, proporciona una propiedad estática de todos los casos que contiene todos los casos de enumeración como una Collection . Simplemente use su propiedad count para saber cuántos casos tiene la enumeración.

Vea la respuesta de Martin para un ejemplo (y vote sus respuestas en lugar de las mías)

Advertencia : el método a continuación ya no parece funcionar.

No conozco ningún método genérico para contar el número de casos enum. Sin embargo, he notado que la propiedad hashValue de los casos enum es incremental, comenzando desde cero y con el orden determinado por el orden en que se declaran los casos. Entonces, el hash de la última enumeración más uno corresponde al número de casos.

Por ejemplo con esta enumeración:

enum Test { case ONE case TWO case THREE case FOUR static var count: Int { return Test.FOUR.hashValue + 1} }

count devuelve 4.

No puedo decir si esa es una regla o si alguna vez cambiará en el futuro, así que úselo bajo su propio riesgo :)


enum EnumNameType: Int { case first case second case third static var count: Int { return EnumNameType.third.rawValue + 1 } } print(EnumNameType.count) //3

O

enum EnumNameType: Int { case first case second case third case count } print(EnumNameType.count.rawValue) //3

* En Swift 4.2 (Xcode 10) puede usar:

enum EnumNameType: CaseIterable { case first case second case third } print(EnumNameType.allCases.count) //3


enum WeekDays : String , CaseIterable { case monday = "Mon" case tuesday = "Tue" case wednesday = "Wed" case thursday = "Thu" case friday = "Fri" case saturday = "Sat" case sunday = "Sun" }

var weekdays = WeekDays.AllCases ()

print ("(weekdays.count)")


struct HashableSequence<T: Hashable>: SequenceType { func generate() -> AnyGenerator<T> { var i = 0 return AnyGenerator { let next = withUnsafePointer(&i) { UnsafePointer<T>($0).memory } if next.hashValue == i { i += 1 return next } return nil } } } extension Hashable { static func enumCases() -> Array<Self> { return Array(HashableSequence()) } static var enumCount: Int { return enumCases().enumCount } } enum E { case A case B case C } E.enumCases() // [A, B, C] E.enumCount // 3

pero tenga cuidado con el uso en tipos que no sean enum. Algunas soluciones podrían ser:

struct HashableSequence<T: Hashable>: SequenceType { func generate() -> AnyGenerator<T> { var i = 0 return AnyGenerator { guard sizeof(T) == 1 else { return nil } let next = withUnsafePointer(&i) { UnsafePointer<T>($0).memory } if next.hashValue == i { i += 1 return next } return nil } } } extension Hashable { static func enumCases() -> Array<Self> { return Array(HashableSequence()) } static var enumCount: Int { return enumCases().count } } enum E { case A case B case C } Bool.enumCases() // [false, true] Bool.enumCount // 2 String.enumCases() // [] String.enumCount // 0 Int.enumCases() // [] Int.enumCount // 0 E.enumCases() // [A, B, C] E.enumCount // 4