Comment puis-je déterminer le nombre de cas dans une énumération rapide?
(Je voudrais éviter énumérer manuellement toutes les valeurs , ou utiliser l'ancien " enum_count trick " si possible)
À partir de Swift 4.2 (Xcode 10), 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
J'ai un article de blog qui donne plus de détails à ce sujet, mais tant que le type brut de votre enum est un entier, vous pouvez ajouter un compte de cette façon:
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
}()
}
Mise à jour Xcode 10
Adoptez le protocole CaseIterable
dans l’énumération, elle fournit une propriété statique allCases
qui contient tous les cas d’énumération en tant que Collection
. Il suffit d’utiliser sa propriété count
pour savoir combien de cas l’énumération a.
Voir la réponse de Martin pour un exemple (et augmenter ses réponses plutôt que les miennes)
Attention: la méthode ci-dessous ne semble plus fonctionner.
Je ne connais aucune méthode générique pour compter le nombre de cas enum. J'ai toutefois remarqué que la propriété hashValue
des observations enum est incrémentielle, à partir de zéro et dans l'ordre déterminé par l'ordre dans lequel les observations sont déclarées. Ainsi, le hash 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
renvoie 4.
Je ne peux pas dire si c'est une règle ou si cela changera un jour, alors utiliser à vos risques et périls
Je définis un protocole réutilisable qui effectue automatiquement le nombre de cas en fonction de l'approche publié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éez un tableau statique allValues comme indiqué dans ce answer
enum ProductCategory : String {
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 d'énumérations entières, vous pouvez ajouter une valeur de membre supplémentaire appelée Count
pour représenter le nombre de membres de l'énum, 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 en appelant TableViewSections.Count.rawValue
qui retournera 2 pour l'exemple ci-dessus.
Lorsque vous manipulez l'énumération dans une instruction switch, assurez-vous de générer un échec d'assertion lorsque vous rencontrez le membre Count
où vous ne vous 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: 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
}
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 donne O(1) plus vous obtenez un test qui échoue si quelqu'un ajoute un enum case five
et ne met pas à jour l'implémentation de count
.
String Enum avec Index
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)!
}
}
compte: eEventTabType.allValues.count
index: objeEventTabType.index
Prendre plaisir :)
Cette fonction repose sur 2 comportements non documentés current (Swift 1.1) enum
:
enum
est simplement un index de case
. Si le nombre de cas est compris entre 2 et 256, il s'agit de UInt8
.enum
a été convertie en bits à partir de l'index de cas invalid, sa hashValue
est 0
Alors utilisez à vos risques et périls :)
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")
}
}
Usage:
enum Foo:String {
case C000 = "foo"
case C001 = "bar"
case C002 = "baz"
}
enumCaseCount(Foo) // -> 3
J'ai écrit une extension simple qui donne à tous les enums où la valeur brute est un entier une propriété 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())
}
}
Malheureusement, il attribue la propriété count
à OptionSetType
s'il ne fonctionne pas correctement. Voici donc une autre version nécessitant la conformité explicite au protocole CaseCountable
pour toute énumération que vous souhaitez 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 publiée par Tom Pelaia, mais ça marche avec tous les types d'entiers.
Bien sûr, ce n’est pas dynamique, mais pour de nombreuses utilisations, vous pouvez vous en tirer avec une variable statique ajoutée à votre Enum
static var count: Int{ return 7 }
puis utilisez-le comme EnumName.count
Pourquoi faites-vous tout cela si complexe? Le compteur SIMPLEST d'Intern est d'ajouter:
case Count
À la fin. Et ... alto - maintenant vous avez le compte - simple et rapide
enum EnumNameType: Int {
case first
case second
case third
static var count: Int { return EnumNameType.third.rawValue + 1 }
}
print(EnumNameType.count) //3
OR
enum EnumNameType: Int {
case first
case second
case third
case count
}
print(EnumNameType.count.rawValue) //3
* Sur Swift 4.2 (Xcode 10), vous pouvez utiliser:
enum EnumNameType: CaseIterable {
case first
case second
case third
}
print(EnumNameType.allCases.count) //3
Pour mon cas d'utilisation, dans une base de code où plusieurs personnes pourraient ajouter 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 l'enum. C'est pour éviter à quelqu'un d'oublier d'ajouter sa clé à la liste de toutes les clés. Faire correspondre le nombre du tableau allKeys (créé initialement en tant qu'ensemble pour éviter les dupes) au nombre de clés de l'énumération garantit qu'elles sont toutes présentes.
Certaines des réponses ci-dessus montrent la voie à suivre dans Swift 2, mais aucune ne fonctionne dans Swift 3 . Voici la version formatée 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)
}
En fonction de votre cas d'utilisation, vous pouvez simplement exécuter le test en développement pour éviter la surcharge liée à l'utilisation de allKeys sur chaque demande.
Si vous ne voulez pas baser votre code dans la dernière énumération, vous pouvez créer cette fonction dans votre énumération.
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
}
A Swift 3 version fonctionnant avec des énumérations de 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.
Le nom générique IntegerType
(renommé Integer
dans Swift 3) n'est pas pris en charge, car il s'agit d'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 Nice, parce que vous n’avez pas besoin de coder en dur une valeur, ce sera instancier chaque valeur enum à chaque appel. C'est O (n) au lieu de O (1).
Autant que je sache, il n’existe actuellement aucune solution de contournement lorsqu’on utilise cela comme extension de protocole (et non dans chaque énumération comme le faisait Nate Cook), car les propriétés stockées statiques ne sont pas prises en charge dans les types génériques.
Quoi qu'il en soit, cela ne devrait pas poser de problème pour les petites circonscriptions. Un cas d'utilisation typique serait le section.count
pour UITableViews
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 facilement être 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 moi-même résolu ce problème en créant un protocole (EnumIntArray) et une fonction d'utilitaire global (enumIntArray) qui facilitent l'ajout d'une variable "All" à n'importe quel enum (à l'aide de Swift 1.2). La variable "all" contiendra un tableau de tous les éléments de l'énumération afin que vous puissiez utiliser all.count pour le nombre
Cela fonctionne uniquement avec les enums qui utilisent des valeurs brutes de type Int, mais peut-être peut-il inspirer d'autres types.
Il aborde également les problèmes de "manque de numérotation" et de "temps excessif pour parcourir" que j'ai lus ci-dessus et ailleurs.
L'idée est d'ajouter le protocole EnumIntArray à votre enum, puis de définir une variable statique "toutes" en appelant la fonction enumIntArray et de lui fournir le premier élément (et le dernier en cas de lacunes dans la numérotation).
Étant donné que la variable statique n’est initialisée qu’une seule fois, la surcharge 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<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
}
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)")
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<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
}
Ensuite, il vous suffit d'appeler Weekdays.allValues.count
.
Ou vous pouvez simplement définir le _count
en dehors de l'énum, 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
}
Ainsi, quel que soit le nombre d’énumérations que vous créez, il ne sera créé qu’une fois.
(supprimez cette réponse si static
le fait)