Comment puis-je obtenir le compte d’une énumération Swift?

Comment puis-je déterminer le nombre de cas dans un enum Swift?

(Je voudrais éviter d’ énumérer manuellement toutes les valeurs ou d’utiliser l’ancien ” enum_count sortingck ” si possible.)

À partir de Swift 4.2 (dernier instantané de développeur), vous pouvez déclarer la conformité au protocole CaseIterable , cela fonctionne pour toutes les énumérations sans valeurs associées:

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

Le nombre de cas est maintenant simplement obtenu avec

 print(Stuff.allCases.count) // 4 

Pour plus d’informations, voir

  • SE-0194 Collection dérivée de cas d’énumération

J’ai un article de blog plus détaillé à ce sujet, mais tant que le type brut de votre enum est un entier, vous pouvez append un compte de cette manière:

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

Je ne suis au courant d’aucune méthode générique pour compter le nombre de cas d’énumération. J’ai cependant remarqué que la propriété hashValue des cas d’énumération est incrémentielle, à partir de zéro, et avec l’ordre déterminé par l’ordre dans lequel les cas sont déclarés. Ainsi, le hachage du dernier enum plus un correspond au nombre de cas.

Par exemple avec cette énumération:

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

count retourne 4.

Je ne peux pas dire si c’est une règle ou si elle changera à l’avenir, alors utilisez à vos risques et périls 🙂

Je définis un protocole réutilisable qui exécute automatiquement le nombre de cas basé sur l’approche postée par 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 } } 

Ensuite, je peux réutiliser ce protocole par exemple comme suit:

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

Créer un tableau allValues ​​statique comme indiqué dans cette réponse

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

Ceci est également utile lorsque vous souhaitez énumérer les valeurs et fonctionne pour tous les types Enum

Si l’implémentation n’a rien contre l’utilisation des énumérations d’entiers, vous pouvez append une valeur de membre supplémentaire appelée Count pour représenter le nombre de membres dans l’énumération – voir l’exemple ci-dessous:

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

Vous pouvez maintenant obtenir le nombre de membres dans l’énumération en appelant TableViewSections.Count.rawValue qui renverra 2 pour l’exemple ci-dessus.

Lorsque vous manipulez l’énumération dans une instruction switch, veillez à lancer un échec d’assertion lorsque vous rencontrez le membre Count auquel vous ne l’attendez pas:

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

Ce type de fonction est capable de retourner le compte de votre enum.

Swift 2 :

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

Swift 3 :

 func enumCount(_: 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 } 

Ssortingng Enum avec Index

 enum eEventTabType : Ssortingng { 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)! } } 

compter: eEventTabType.allValues.count

index: objeEventTabType.index

Prendre plaisir 🙂

Oh hey tout le monde, qu’en est-il des tests unitaires?

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

Ceci combiné avec la solution d’Antonio:

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

dans le code principal, vous obtenez O (1) et vous obtenez un test d’échec si quelqu’un ajoute un cas d’énumération five et ne met pas à jour l’implémentation de count .

Cette fonction repose sur 2 comportements enum (Swift 1.1) courants non documentés:

  • La mise en mémoire de l’ enum n’est qu’un index de la case . Si le nombre de cas est compris entre 2 et 256, c’est UInt8 .
  • Si l’ enum été convertie en bits à partir d’un index de cas invalide , sa valeur hashValue est 0

Alors utilisez à vos risques et périls 🙂

 func enumCaseCount(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") } } 

Usage:

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

Bien sûr, ce n’est pas dynamic, mais pour de nombreuses utilisations, vous pouvez vous en sortir avec une variable statique ajoutée à votre Enum.

static var count: Int{ return 7 }

puis utilisez-le comme EnumName.count

J’ai écrit une extension simple qui donne toutes les énumérations où la valeur brute est un nombre entier:

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

Malheureusement, il donne la propriété count à OptionSetType où il ne fonctionnera pas correctement, alors voici une autre version qui nécessite une conformité explicite au protocole CaseCountable pour toute énumération dont vous voulez compter:

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

C’est très similaire à l’approche de Tom Pelaia, mais fonctionne avec tous les types d’entiers.

Pour mon cas d’utilisation, dans une base de code où plusieurs personnes pourraient append des clés à une énumération et que ces cas devraient tous être disponibles dans la propriété allKeys, il est important que toutes les clés soient validées par rapport aux clés de la liste. Ceci afin d’éviter que quelqu’un oublie d’append sa clé à la liste de toutes les clés. Associer le nombre de tableaux allKeys (créés en premier lieu pour éviter les dupes) au nombre de clés de l’énum garantit qu’ils sont tous présents.

Certaines des réponses ci-dessus montrent la manière d’y parvenir dans Swift 2, mais aucune ne fonctionne dans Swift 3 . Voici la version formatée de Swift 3 :

 static func enumCount(_ 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 = [.all, .your, .cases, .here] guard keys.count == enumSize else { fatalError("Missmatch between allKeys(\(keys.count)) and actual keys(\(enumSize)) in enum.") } return Array(keys) } 

Selon votre cas d’utilisation, vous souhaiterez peut-être exécuter le test en développement pour éviter l’utilisation de allKeys à chaque requête.

Pourquoi vous rendez tout cela si complexe? Le compteur SIMPLEST d’Int Enum consiste à append:

case Count

À la fin. Et … alto – maintenant vous avez le compte – rapide et simple

Si vous ne voulez pas baser votre code dans le dernier enum, vous pouvez créer cette fonction dans votre enum.

 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 } 

Une version Swift 3 fonctionnant avec les Int type 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édits: Basé sur les réponses de bzz et Nate Cook.

IntegerType générique (dans Swift 3 renommé en Integer ) n’est pas supporté, car c’est un type générique très fragmenté qui manque de nombreuses fonctions. successor n’est plus disponible avec Swift 3.

Sachez que le commentaire de Code Commander à la réponse de Nate Cooks est toujours valide:

Bien que vous n’ayez pas besoin de coder en dur une valeur, cela instanciera chaque valeur d’énumération à chaque appel. C’est O (n) au lieu de O (1).

Pour autant que je sache, il n’y a actuellement aucune solution de contournement lors de l’utilisation de cette extension de protocole (et de ne pas l’implémenter dans chaque énumération comme Nate Cook), car les propriétés statiques stockées ne sont pas sockets en charge dans les types génériques.

Quoi qu’il en soit, pour les petites énumérations, cela ne devrait pas poser de problème. Un cas d’utilisation typique serait le section.count pour UITableViews comme déjà mentionné par Zorayr.

En prolongeant la réponse de Matthieu Riegler, il s’agit d’une solution pour Swift 3 qui ne nécessite pas l’utilisation de génériques et qui peut être facilement appelée en utilisant le type enum avec 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 } 

J’ai résolu ce problème pour moi-même en créant un protocole (EnumIntArray) et une fonction d’utilitaire globale (enumIntArray) qui facilitent l’ajout d’une variable “All” à n’importe quelle énumération (en utilisant swift 1.2). La variable “all” contiendra un tableau de tous les éléments de l’enum pour que vous puissiez utiliser all.count pour le compte

Cela ne fonctionne qu’avec les énumérations qui utilisent des valeurs brutes de type Int mais peut-être peut-être une source d’inspiration pour d’autres types.

Il aborde également le problème de “la numérotation” et du “temps excessif pour itérer” que j’ai lu ci-dessus et ailleurs.

L’idée est d’append le protocole EnumIntArray à votre enum, puis de définir une variable statique “all” en appelant la fonction enumIntArray et de lui fournir le premier élément (et le dernier s’il y a des lacunes dans la numérotation).

Étant donné que la variable statique n’est initialisée qu’une seule fois, le dépassement de toutes les valeurs brutes n’atteint votre programme qu’une seule fois.

exemple (sans lacunes):

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

exemple (avec des lacunes):

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

Voici le code qui l’implémente:

 protocol EnumIntArray { init?(rawValue:Int) var rawValue:Int { get } } func 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 } 

Ou vous pouvez simplement définir le _count dehors de l’énumération, et l’attacher statiquement:

 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 cette façon, peu importe le nombre de énumérations que vous créez, il ne sera jamais créé qu’une seule fois.

(supprimer cette réponse si static fait)

J’aime la réponse acceptée. Cependant, pour ceux qui veulent coder en dur la valeur de compte mais utilisent un type de chaîne enum plutôt qu’Int, il rest le moyen simple de:

 enum Test: Ssortingng { case One = "One" case Two = "Two" case Three = "Three" case Four = "Four" case count = "4" } 

Y accéder

  guard let enumCount = Int(Test.count.rawValue) else { return } 

La méthode suivante provient de CoreKit et est similaire aux réponses suggérées par d’autres. Cela fonctionne avec Swift 4.

 public protocol EnumCollection: Hashable { static func cases() -> AnySequence static var allValues: [Self] { get } } public extension EnumCollection { public static func cases() -> AnySequence { return AnySequence { () -> AnyIterator 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: Ssortingng, EnumCollection { case sunday, monday, tuesday, wednesday, thursday, friday, saturday } 

Ensuite, il vous suffit d’appeler simplement Weekdays.allValues.count .

 struct HashableSequence: SequenceType { func generate() -> AnyGenerator { var i = 0 return AnyGenerator { let next = withUnsafePointer(&i) { UnsafePointer($0).memory } if next.hashValue == i { i += 1 return next } return nil } } } extension Hashable { static func enumCases() -> Array { 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 

mais soyez prudent avec l’utilisation sur les types non-enum. Une solution pourrait être:

 struct HashableSequence: SequenceType { func generate() -> AnyGenerator { var i = 0 return AnyGenerator { guard sizeof(T) == 1 else { return nil } let next = withUnsafePointer(&i) { UnsafePointer($0).memory } if next.hashValue == i { i += 1 return next } return nil } } } extension Hashable { static func enumCases() -> Array { 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 Ssortingng.enumCases() // [] Ssortingng.enumCount // 0 Int.enumCases() // [] Int.enumCount // 0 E.enumCases() // [A, B, C] E.enumCount // 4 

Il peut utiliser une constante statique qui contient la dernière valeur de l’énumération plus une.

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

Ceci est mineur, mais je pense qu’une meilleure solution O (1) serait la suivante ( UNIQUEMENT si votre enum est Int commençant à 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 réponse sélectionnée actuelle, je crois toujours, est la meilleure réponse pour tous les énumérations, à moins que vous ne travailliez avec Int alors je recommande cette solution.