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 decase
ycase
. Si el recuento de casos es de 2 a 256, esUInt8
. -
Si la
enum
seenum
bits a partir de un índice de caso no válido , su valorhashValue
es0
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