web-dev-qa-db-fra.com

Comment énumérer une énumération avec le type String?

enum Suit: String {
    case spades = "♠"
    case hearts = "♥"
    case diamonds = "♦"
    case clubs = "♣"
}

Par exemple, comment puis-je faire quelque chose comme:

for suit in Suit {
    // do something with suit
    print(suit.rawValue)
}

Exemple résultant:

♠
♥
♦
♣
469
Lucien

Swift 4.2+

À partir de Swift 4.2 (avec Xcode 10), ajoutez simplement la conformité du protocole à CaseIterable pour bénéficier de allCases:

extension Suit: CaseIterable {}

Ensuite, toutes les valeurs possibles seront imprimées:

Suit.allCases.forEach {
    print($0.rawValue)
}

Compatibilité avec les versions antérieures de Swift (3.x et 4.x)

Juste imiter l'implémentation de Swift 4.2:

#if !Swift(>=4.2)
public protocol CaseIterable {
    associatedtype AllCases: Collection where AllCases.Element == Self
    static var allCases: AllCases { get }
}
extension CaseIterable where Self: Hashable {
    static var allCases: [Self] {
        return [Self](AnySequence { () -> AnyIterator<Self> in
            var raw = 0
            var first: Self?
            return AnyIterator {
                let current = withUnsafeBytes(of: &raw) { $0.load(as: Self.self) }
                if raw == 0 {
                    first = current
                } else if current == first {
                    return nil
                }
                raw += 1
                return current
            }
        })
    }
}
#endif
172
Cœur

Cet article est pertinent ici https://www.Swift-studies.com/blog/2014/6/10/enumerating-enums-in-Swift

La solution proposée est essentiellement

enum ProductCategory : String {
     case Washers = "washers", Dryers = "dryers", Toasters = "toasters"

     static let allValues = [Washers, Dryers, Toasters]
}

for category in ProductCategory.allValues{
     //Do something
}
504
rougeExciter

J'ai créé une fonction utilitaire iterateEnum() pour itérer des observations pour des types enum arbitraires.

Voici l'exemple d'utilisation:

enum Suit:String {
    case Spades = "♠"
    case Hearts = "♥"
    case Diamonds = "♦"
    case Clubs = "♣"
}

for f in iterateEnum(Suit) {
    println(f.rawValue)
}

les sorties:

♠
♥
♦
♣

Mais cela ne concerne que uniquement à des fins de débogage ou de test: Cela repose sur plusieurs comportements de compilateur actuels non documentés (Swift1.1). Alors, utilisez-le à vos risques et périls :)

Voici le code:

func iterateEnum<T: Hashable>(_: T.Type) -> GeneratorOf<T> {
    var cast: (Int -> T)!
    switch sizeof(T) {
    case 0: return GeneratorOf(GeneratorOfOne(unsafeBitCast((), T.self)))
    case 1: cast = { unsafeBitCast(UInt8(truncatingBitPattern: $0), T.self) }
    case 2: cast = { unsafeBitCast(UInt16(truncatingBitPattern: $0), T.self) }
    case 4: cast = { unsafeBitCast(UInt32(truncatingBitPattern: $0), T.self) }
    case 8: cast = { unsafeBitCast(UInt64($0), T.self) }
    default: fatalError("cannot be here")
    }

    var i = 0
    return GeneratorOf {
        let next = cast(i)
        return next.hashValue == i++ ? next : nil
    }
}

L'idée sous-jacente est:

  • La représentation en mémoire de enum - à l'exclusion de enums avec les types associés - est simplement un index de cas, lorsque le nombre de cas est 2...256, il est identique à UInt8, quand 257...65536, c'est UInt16, etc. Donc, il peut s'agir de unsafeBitcast parmi les types entiers non signés correspondants.
  • .hashValue of enum values ​​est identique à l'index du cas.
  • .hashValue des valeurs énumérées bitcastées à partir de invalide index est 0

AJOUTÉE:

Révisé pour Swift2 et mis en œuvre des idées de casting de @ Kametrixom's answer

func iterateEnum<T: Hashable>(_: T.Type) -> AnyGenerator<T> {
    var i = 0
    return anyGenerator {
        let next = withUnsafePointer(&i) { UnsafePointer<T>($0).memory }
        return next.hashValue == i++ ? next : nil
    }
}

ADDED: Révisé pour Swift3

func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
    var i = 0
    return AnyIterator {
        let next = withUnsafePointer(to: &i) {
            $0.withMemoryRebound(to: T.self, capacity: 1) { $0.pointee }
        }
        if next.hashValue != i { return nil }
        i += 1
        return next
    }
}

ADDED: Révisé pour Swift3.0.1

func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
    var i = 0
    return AnyIterator {
        let next = withUnsafeBytes(of: &i) { $0.load(as: T.self) }
        if next.hashValue != i { return nil }
        i += 1
        return next
    }
}
272
rintaro

Les autres solutions fonctionnent mais elles supposent toutes, par exemple, le nombre de rangs et de combinaisons possibles, ou ce que peuvent être le premier et le dernier rang. Certes, la disposition d'un jeu de cartes ne changera probablement pas beaucoup dans un avenir proche. En général, cependant, il est préférable d'écrire du code qui suppose le moins possible d'hypothèses. Ma solution:

J'ai ajouté un type brut à l'énumération de costume, je peux donc utiliser Suit (rawValue :) pour accéder aux cas de Suit:

enum Suit: Int {
    case Spades = 1
    case Hearts, Diamonds, Clubs
    func simpleDescription() -> String {
        switch self {
            case .Spades:
                return "spades"
            case .Hearts:
                return "hearts"
            case .Diamonds:
                return "diamonds"
            case .Clubs:
                return "clubs"
        }
    }
    func color() -> String {
        switch self {
        case .Spades:
            return "black"
        case .Clubs:
            return "black"
        case .Diamonds:
            return "red"
        case .Hearts:
            return "red"
        }
    }
}

enum Rank: Int {
    case Ace = 1
    case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
    case Jack, Queen, King
    func simpleDescription() -> String {
        switch self {
            case .Ace:
                return "ace"
            case .Jack:
                return "jack"
            case .Queen:
                return "queen"
            case .King:
                return "king"
            default:
                return String(self.rawValue)
        }
    }
}

Ci-dessous l'implémentation de la méthode createDeck () de Card. init (rawValue :) est un initialiseur disponible et retourne un optionnel. En décompressant et en vérifiant sa valeur dans les deux déclarations while, il n'est pas nécessaire de supposer le nombre de cas Rank ou Suit:

struct Card {
    var rank: Rank
    var suit: Suit
    func simpleDescription() -> String {
        return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
    }
    func createDeck() -> [Card] {
        var n = 1
        var deck = [Card]()
        while let rank = Rank(rawValue: n) {
            var m = 1
            while let suit = Suit(rawValue: m) {
                deck.append(Card(rank: rank, suit: suit))
                m += 1
            }
            n += 1
        }
        return deck
    }
}

Voici comment appeler la méthode createDeck:

let card = Card(rank: Rank.Ace, suit: Suit.Clubs)
let deck = card.createDeck()
126
sdduursma

La deuxième réponse qui fonctionne vraiment

Donc, je suis tombé sur les bits et les octets et j'ai créé une extension (que j'ai découvert par la suite, fonctionne très similaire à la réponse de @rintaro ). C'est utilisable comme ça:

enum E : EnumCollection {
    case A, B, C
}

Array(E.cases())    // [A, B, C]

Ce qui est remarquable, c’est qu’il est utilisable sur n’importe quel enum (sans valeurs associées). Notez que cela ne fonctionne pas pour les enums qui n'ont pas de cas.

Avertissement

Comme avec la réponse de @rintaro , ce code utilise la représentation sous-jacente d'un enum. Cette représentation n'est pas documentée et pourrait changer dans le futur, ce qui la casserait -> Je ne recommande pas son utilisation en production. 

Code (Swift 2.2, Xcode 7.3.1, ne fonctionne pas sur Xcode 10)

protocol EnumCollection : Hashable {}
extension EnumCollection {
    static func cases() -> AnySequence<Self> {
        typealias S = Self
        return AnySequence { () -> AnyGenerator<S> in
            var raw = 0
            return AnyGenerator {
                let current : Self = withUnsafePointer(&raw) { UnsafePointer($0).memory }
                guard current.hashValue == raw else { return nil }
                raw += 1
                return current
            }
        }
    }
}

Code (Swift 3, Xcode 8.1, ne fonctionne pas sur Xcode 10)

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

(Je ne sais pas pourquoi j'ai besoin de typealias, mais le compilateur se plaint de ne pas en avoir)

(J'ai apporté de grandes modifications à cette réponse, regardez les modifications des versions antérieures)

71
Kametrixom

Vous pouvez parcourir une énumération en implémentant le protocole ForwardIndexType.

Le protocole ForwardIndexType nécessite que vous définissiez une fonction successor() pour parcourir les éléments.

enum Rank: Int, ForwardIndexType {
    case Ace = 1
    case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
    case Jack, Queen, King

    // ... other functions

    // Option 1 - Figure it out by hand
    func successor() -> Rank {
        switch self {
            case .Ace:
              return .Two
            case .Two:
              return .Three

            // ... etc.

            default:
              return .King
        }
    }

    // Option 2 - Define an operator!
    func successor() -> Rank {
        return self + 1
    }
}

// NOTE: The operator is defined OUTSIDE the class
func + (left: Rank, right: Int) -> Rank {
    // I'm using to/from raw here, but again, you can use a case statement
    // or whatever else you can think of

    return left == .King ? .King : Rank(rawValue: left.rawValue + right)!
}

Une itération sur une plage ouverte ou fermée (..< ou ...) appelle en interne la fonction successor() qui vous permet d'écrire ceci:

// Under the covers, successor(Rank.King) and successor(Rank.Ace) are called to establish limits
for r in Rank.Ace...Rank.King {
    // Do something useful
}
25
RndmTsk

En principe, il est possible de le faire de cette manière en supposant que vous n'utilisez pas l'affectation de valeurs brutes pour les cas d'enum:

enum RankEnum: Int {
  case Ace
  case One
  case Two
}

class RankEnumGenerator : Generator {
  var i = 0
  typealias Element = RankEnum
  func next() -> Element? {
    let r = RankEnum.fromRaw(i)
    i += 1
    return r
  }
}

extension RankEnum {
  static func enumerate() -> SequenceOf<RankEnum> {
    return SequenceOf<RankEnum>({ RankEnumGenerator() })
  }
}

for r in RankEnum.enumerate() {
  println("\(r.toRaw())")
}
17
Alfa07

Si vous donnez l'énum une valeur Int brute cela facilitera grandement la lecture en boucle.

Par exemple, vous pouvez utiliser anyGenerator pour obtenir un générateur pouvant énumérer vos valeurs:

enum Suit: Int, CustomStringConvertible {
    case Spades, Hearts, Diamonds, Clubs
    var description: String {
        switch self {
        case .Spades:   return "Spades"
        case .Hearts:   return "Hearts"
        case .Diamonds: return "Diamonds"
        case .Clubs:    return "Clubs"
        }
    }
    static func enumerate() -> AnyGenerator<Suit> {
        var nextIndex = Spades.rawValue
        return anyGenerator { Suit(rawValue: nextIndex++) }
    }
}
// You can now use it like this:
for suit in Suit.enumerate() {
    suit.description
}
// or like this:
let allSuits: [Suit] = Array(Suit.enumerate())

Cependant, cela ressemble à un modèle assez commun, ne serait-ce pas Nice si nous pouvions rendre tout type énumérable énumérable en se conformant simplement à un protocole? Eh bien avec Swift 2.0 et les extensions de protocole, maintenant nous le pouvons!

Ajoutez simplement ceci à votre projet:

protocol EnumerableEnum {
    init?(rawValue: Int)
    static func firstValue() -> Int
}
extension EnumerableEnum {
    static func enumerate() -> AnyGenerator<Self> {
        var nextIndex = firstRawValue()
        return anyGenerator { Self(rawValue: nextIndex++) }
    }
    static func firstRawValue() -> Int { return 0 }
}

Maintenant, chaque fois que vous créez une énumération (tant qu'elle a une valeur brute Int), vous pouvez la rendre énumérable en vous conformant au protocole:

enum Rank: Int, EnumerableEnum {
    case Ace, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King
}
// ...
for rank in Rank.enumerate() { ... }

Si vos valeurs enum ne commencent pas par 0 (valeur par défaut), remplacez la méthode firstRawValue:

enum DeckColor: Int, EnumerableEnum {
    case Red = 10, Blue, Black
    static func firstRawValue() -> Int { return Red.rawValue }
}
// ...
let colors = Array(DeckColor.enumerate())

La classe Suit finale, comprenant le remplacement de simpleDescription par le protocole plus standard, CustomStringConvertible , ressemblera à ceci:

enum Suit: Int, CustomStringConvertible, EnumerableEnum {
    case Spades, Hearts, Diamonds, Clubs
    var description: String {
        switch self {
        case .Spades:   return "Spades"
        case .Hearts:   return "Hearts"
        case .Diamonds: return "Diamonds"
        case .Clubs:    return "Clubs"
        }
    }
}
// ...
for suit in Suit.enumerate() {
    print(suit.description)
}

MODIFIER:

Swift 3 syntaxe:

protocol EnumerableEnum {
    init?(rawValue: Int)
    static func firstRawValue() -> Int
}

extension EnumerableEnum {
    static func enumerate() -> AnyIterator<Self> {
        var nextIndex = firstRawValue()

        let iterator: AnyIterator<Self> = AnyIterator {
            defer { nextIndex = nextIndex + 1 }
            return Self(rawValue: nextIndex)
        }

        return iterator
    }

    static func firstRawValue() -> Int {
        return 0
    }
}
13
Senseful

Mis à jour à Swift 2.2 +

func iterateEnum<T: Hashable>(_: T.Type) -> AnyGenerator<T> {
    var i = 0
    return AnyGenerator {
        let next = withUnsafePointer(&i) {
            UnsafePointer<T>($0).memory
        }
        if next.hashValue == i {
            i += 1
            return next
        } else {
            return nil
        }
    }
}

son code est mis à jour au format Swift 2.2 @ @ Kametrixom swer

Pour Swift 3.0+ (merci beaucoup à @Philip )

func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
    var i = 0
    return AnyIterator {
        let next = withUnsafePointer(&i) {
            UnsafePointer<T>($0).pointee
        }
        if next.hashValue == i {
            i += 1
            return next
        } else {
            return nil
        }
    }
}
12
ale_stro

Ce problème est maintenant beaucoup plus facile. Voici ma solution Swift 4.2.

enum Suit: Int, CaseIterable {
    case None
    case Spade, Heart, Diamond, Club

    static let allNonNullCases = Suit.allCases[Spade.rawValue...]
}

enum Rank: Int, CaseIterable {
    case Joker
    case Two, Three, Four, Five, Six, Seven, Eight
    case Nine, Ten, Jack, Queen, King, Ace

    static let allNonNullCases = Rank.allCases[Two.rawValue...]
}

func makeDeck(withJoker: Bool = false) -> [Card] {
    var deck = [Card]()
    for suit in Suit.allNonNullCases {
        for rank in Rank.allNonNullCases {
            deck.append(Card(suit: suit, rank: rank))
        }
    }
    if withJoker {
        deck.append(Card(suit: .None, rank: .Joker))
    }
    return deck
}

Pre 4.2

J'aime cette solution que j'ai mise en place après avoir trouvé cette page: Compréhension de liste dans Swift

Il utilise Int raw au lieu de Strings, mais évite de taper deux fois, il permet de personnaliser les plages et ne code pas en dur les valeurs brutes.

Ceci est la version 4 de Swift de ma solution d'origine mais voyez l'amélioration 4.2 ci-dessus.

enum Suit: Int {
    case None
    case Spade, Heart, Diamond, Club

    static let allRawValues = Suit.Spade.rawValue...Suit.Club.rawValue
    static let allCases = Array(allRawValues.map{ Suit(rawValue: $0)! })
}
enum Rank: Int {
    case Joker
    case Two, Three, Four, Five, Six
    case Seven, Eight, Nine, Ten
    case Jack, Queen, King, Ace

    static let allRawValues = Rank.Two.rawValue...Rank.Ace.rawValue
    static let allCases = Array(allRawValues.map{ Rank(rawValue: $0)! })
}
func makeDeck(withJoker: Bool = false) -> [Card] {
    var deck = [Card]()
    for suit in Suit.allCases {
        for rank in Rank.allCases {
            deck.append(Card(suit: suit, rank: rank))
        }
    }
    if withJoker {
        deck.append(Card(suit: .None, rank: .Joker))
    }
    return deck
}
11
adazacom

Je me suis retrouvé en train de faire .allValues beaucoup tout au long de mon code. J'ai finalement trouvé un moyen de simplement me conformer à un protocole Iteratable et d'avoir une méthode rawValues().

protocol Iteratable {}
extension RawRepresentable where Self: RawRepresentable {

    static func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
        var i = 0
        return AnyIterator {
            let next = withUnsafePointer(to: &i) {
                $0.withMemoryRebound(to: T.self, capacity: 1) { $0.pointee }
            }
            if next.hashValue != i { return nil }
            i += 1
            return next
        }
    }
}

extension Iteratable where Self: RawRepresentable, Self: Hashable {
    static func hashValues() -> AnyIterator<Self> {
        return iterateEnum(self)
    }

    static func rawValues() -> [Self.RawValue] {
        return hashValues().map({$0.rawValue})
    }
}


// Example
enum Grocery: String, Iteratable {
    case Kroger = "kroger"
    case HEB = "h.e.b."
    case Randalls = "randalls"
}

let groceryHashes = Grocery.hashValues() // AnyIterator<Grocery>
let groceryRawValues = Grocery.rawValues() // ["kroger", "h.e.b.", "randalls"]
7
Fabian Buentello

EDIT: Proposition de Swift EvolutionSE-0194 Collection dérivée de cas Enum propose un niveau solution à ce problème. Nous le voyons dans Swift 4.2 et plus récent. La proposition souligne également certaines solutions de contournement qui sont similaires à certaines déjà mentionnées ici, mais il pourrait être intéressant de voir néanmoins.

Je conserverai également mon message original pour des raisons de complétude.


C'est encore une autre approche basée sur la réponse de @ Peymmankh, adaptée à Swift 3.

public protocol EnumCollection : Hashable {}

extension EnumCollection {

public static func allValues() -> [Self] {
    typealias S = Self

    let retVal = AnySequence { () -> AnyIterator<S> in
        var raw = 0
        return AnyIterator {
            let current = withUnsafePointer(to: &raw) {
                 $0.withMemoryRebound(to: S.self, capacity: 1) { $0.pointee }
            }
            guard current.hashValue == raw else { return nil }
            raw += 1
            return current
        }
    }

    return [S](retVal)
  }
}
6
ff10
enum Rank: Int {
    ...
    static let ranks = (Rank.Ace.rawValue ... Rank.King.rawValue).map{Rank(rawValue: $0)! }

}
enum Suit {
    ...
    static let suits = [Spades, Hearts, Diamonds, Clubs]
}

struct Card {
    ...
    static func fullDesk() -> [Card] {
        var desk: [Card] = []
        for suit in Suit.suits {
            for rank in Rank.ranks {
                desk.append(Card(rank: rank,suit: suit))
            }
        }
        return desk
    }
}

Que dis-tu de ça?

5
Mossila

Désolé, ma réponse était spécifique à la façon dont j'ai utilisé ce message pour ce que je devais faire. Pour ceux qui trébuchent sur cette question, cherchant un moyen de trouver un cas dans une énumération, voici le moyen de le faire (nouveau dans Swift 2):

Edit: minuscule camelCase est maintenant la norme pour les valeurs Swift 3 enum

// From Apple docs: If the raw-value type is specified as String and you don’t assign values to the cases explicitly, each unassigned case is implicitly assigned a string with the same text as the name of that case.

enum Theme: String
    {
    case white, blue, green, lavender, grey
    }

func loadTheme(theme: String)
    {
    // this checks the string against the raw value of each enum case (note that the check could result in a nil value, since it's an optional, which is why we introduce the if/let block
    if let testTheme = Theme(rawValue: theme)
        {
        // testTheme is guaranteed to have an enum value at this point
        self.someOtherFunction(testTheme)
        }
    }

Pour ceux qui s’interrogent sur l’énumération sur une énumération, les réponses données sur cette page qui incluent un var/let statique contenant un tableau de toutes les valeurs d’énum sont correctes. Le dernier exemple de code Apple pour tvOS contient exactement la même technique.

Cela étant dit, ils devraient créer un mécanisme plus pratique dans le langage (Apple, est-ce que vous écoutez?)!

4
Gene Loparco

Dans Swift 3, lorsque l'énum sous-jacent a {rawValue} s, vous pouvez implémenter le protocole {Strideable}. Les avantages sont qu'aucun tableau de valeurs n'est créé comme dans d'autres suggestions et que l'instruction Swift standard "for i in ..." fonctionne, ce qui donne la syntaxe Nice.

// "Int" to get rawValue, and {Strideable} so we can iterate
enum MyColorEnum : Int, Strideable {
    case Red
    case Green
    case Blue
    case Black

    //-------- required by {Strideable}
    typealias Stride = Int

    func advanced(by n:Stride) -> MyColorEnum {
        var next = self.rawValue + n
        if next > MyColorEnum.Black.rawValue {
            next = MyColorEnum.Black.rawValue
        }
        return MyColorEnum(rawValue: next)!
    }

    func distance(to other: MyColorEnum) -> Int {
        return other.rawValue - self.rawValue
    }

    //-------- just for printing
    func simpleDescription() -> String {
        switch self {
        case .Red: return "Red"
        case .Green: return "Green"
        case .Blue: return "Blue"
        case .Black: return "Black"
        }
    }
}

// this is how you use it:
for i in MyColorEnum.Red ... MyColorEnum.Black {
    print("ENUM: \(i)")
}
4
ACK

Vous pouvez essayer d'énumérer comme ça

enum Planet: String {
  case Mercury
  case Venus
  case Earth
  case Mars

static var enumerate: [Planet] {
    var a: [Planet] = []
    switch Planet.Mercury {
    case .Mercury: a.append(.Mercury); fallthrough
    case .Venus: a.append(.Venus); fallthrough
    case .Earth: a.append(.Earth); fallthrough
    case .Mars: a.append(.Mars)
    }
    return a
  }
}

Planet.enumerate // [Mercury, Venus, Earth, Mars]
4
Karthik Kumar

C'est ce que j'ai fini par aller avec; Je pense que cela établit un juste équilibre entre lisibilité et maintenabilité.

struct Card {

// ...

static func deck() -> Card[] {
    var deck = Card[]()
    for rank in Rank.Ace.toRaw()...Rank.King.toRaw() {
        for suit in [Suit.Spades, .Hearts, .Clubs, .Diamonds] {
            let card = Card(rank: Rank.fromRaw(rank)!, suit: suit)
            deck.append(card)
        }
    }
    return deck
}

let deck = Card.deck()
4
Andrew

Xcode 10 avec Swift 4.2

enum Filter: String, CaseIterable {

    case salary = "Salary"
    case experience = "Experience"
    case technology = "Technology"
    case unutilized = "Unutilized"
    case unutilizedHV = "Unutilized High Value"

    static let allValues = Filter.allCases.map { $0.rawValue }
}

Appeler

print(Filter.allValues)

Impressions:

["Salaire", "Expérience", "Technologie", "Inutilisé", "Haute valeur inutilisée"]


Versions plus anciennes

Pour enum représentant Int

enum Filter: Int {
    case salary
    case experience
    case technology
    case unutilized
    case unutilizedHV

    static let allRawValues = salary.rawValue...unutilizedHV.rawValue  // First to last case
    static let allValues = allRawValues.map { Filter(rawValue: $0)!.rawValue }
}

Appelez ça comme ça:

print(Filter.allValues)

Impressions:

[0, 1, 2, 3, 4]


Pour enum représentant String

enum Filter: Int {
    case salary
    case experience
    case technology
    case unutilized
    case unutilizedHV

    static let allRawValues = salary.rawValue...unutilizedHV.rawValue  // First to last case
    static let allValues = allRawValues.map { Filter(rawValue: $0)!.description }
}

extension Filter: CustomStringConvertible {
    var description: String {
        switch self {
        case .salary: return "Salary"
        case .experience: return "Experience"
        case .technology: return "Technology"
        case .unutilized: return "Unutilized"
        case .unutilizedHV: return "Unutilized High Value"
        }
    }
}

Appeler

print(Filter.allValues)

Impressions:

["Salaire", "Expérience", "Technologie", "Inutilisé", "Haute valeur inutilisée"]

3
Warif Akhand Rishi

L'expérience était: EXPERIMENT

Ajoutez une méthode à la carte qui crée un jeu complet de cartes, avec une carte de chaque combinaison de rang et de couleur.

Donc, sans modifier ni améliorer le code donné autrement qu'en ajoutant la méthode (et sans utiliser des éléments qui n'ont pas encore été enseignés), j'ai proposé cette solution:

struct Card {
    var rank: Rank
    var suit: Suit

    func simpleDescription() -> String {
        return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
    }

    func createDeck() -> [Card] {
        var deck: [Card] = []
        for rank in Rank.Ace.rawValue...Rank.King.rawValue {
            for suit in Suit.Spades.rawValue...Suit.Clubs.rawValue {
                let card = Card(rank: Rank(rawValue: rank)!, suit: Suit(rawValue: suit)!)
                //println(card.simpleDescription())
                deck += [card]
            }
        }
        return deck
    }
}
let threeOfSpades = Card(rank: .Three, suit: .Spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()
let deck = threeOfSpades.createDeck()
3
Hans-Peter

Tout en traitant de Swift 2.0 voici ma suggestion:

J'ai ajouté le type brut à Suitenum 

enum Suit: Int {

puis:

struct Card {
    var rank: Rank
    var suit: Suit


    func fullDeck()-> [Card] {

        var deck = [Card]()

        for i in Rank.Ace.rawValue...Rank.King.rawValue {

            for j in Suit.Spades.rawValue...Suit.Clubs.rawValue {

                deck.append(Card(rank:Rank(rawValue: i)! , suit: Suit(rawValue: j)!))
            }
        }

        return deck
    }
}
2
Aladin

Comme avec @Kametrixom answer here Je pense que retourner un tableau serait mieux que de retourner AnySequence, car vous pouvez avoir accès à tous les goodies d'Array tels que count, etc.

Voici la réécriture:

public protocol EnumCollection : Hashable {}
extension EnumCollection {
    public static func allValues() -> [Self] {
        typealias S = Self
        let retVal = AnySequence { () -> AnyGenerator<S> in
            var raw = 0
            return AnyGenerator {
                let current : Self = withUnsafePointer(&raw) { UnsafePointer($0).memory }
                guard current.hashValue == raw else { return nil }
                raw += 1
                return current
            }
        }

        return [S](retVal)
    }
}
2
Peymankh

Je l'ai fait en utilisant la propriété calculée, qui renvoie le tableau de toutes les valeurs (grâce à cet article http://natecook.com/blog/2014/10/loopy-random-enum-ideas/ ). Cependant, il utilise également int raw-values, mais je n'ai pas besoin de répéter tous les membres de l'énumération dans une propriété distincte. 

UPDATE Xcode 6.1 a un peu changé la façon d'obtenir un membre enum en utilisant une valeur brute, j'ai donc corrigé le listing. Également corrigé une petite erreur avec une première valeur brute incorrecte

enum ValidSuits:Int{
    case Clubs=0, Spades, Hearts, Diamonds
    func description()->String{
        switch self{
        case .Clubs:
            return "♣︎"
        case .Spades:
            return "♠︎"
        case .Diamonds:
            return "♦︎"
        case .Hearts:
            return "♥︎"
        }
    }

    static var allSuits:[ValidSuits]{
        return Array(
            SequenceOf {
                () -> GeneratorOf<ValidSuits> in
                var i=0
                return GeneratorOf<ValidSuits>{
                    return ValidSuits(rawValue: i++)
                }
            }
        )
    }
}
2
gleb vodovozov

Les énumérations ont les méthodes toRaw () et fromRaw (). Ainsi, si votre valeur brute est un Int, vous pouvez effectuer une itération du premier au dernier enum:

enum Suit: Int {
    case Spades = 1
    case Hearts, Diamonds, Clubs
    func simpleDescription() -> String {
        switch self {
        case .Spades:
            return "spades"
        case .Hearts:
            return "hearts"
        case .Diamonds:
            return "diamonds"
        case .Clubs:
            return "clubs"
        }
    }
}

for i in Suit.Spades.toRaw()...Suit.Clubs.toRaw() {
    if let covertedSuit = Suit.fromRaw(i) {
        let description = covertedSuit.simpleDescription()
    }
}

Un des pièges est que vous devez tester les valeurs facultatives avant d'exécuter la méthode simpleDescription. Nous définissons donc d'abord converti comme suit pour notre valeur, puis définissons une constante sur conversSuit.simpleDescription ().

2

Ceci est un joli vieux post, de Swift 2.0. Il existe maintenant de meilleures solutions qui utilisent les nouvelles fonctionnalités de Swift 3.0: Itérer dans un Enum dans Swift 3.0

Et sur cette question, il existe une solution qui utilise une nouvelle fonctionnalité de (le pas encore publié, au moment où j'écris cette édition) Swift 4.2: Comment puis-je obtenir le compte d'un enum Swift?


Il y a beaucoup de bonnes solutions dans ce fil et d'autres, mais certaines sont très compliquées. J'aime simplifier autant que possible. Voici une solution qui peut ou peut ne pas fonctionner pour des besoins différents mais je pense que cela fonctionne bien dans la plupart des cas: 

enum Number: String {
    case One
    case Two
    case Three
    case Four
    case EndIndex

    func nextCase () -> Number
    {
        switch self {
        case .One:
            return .Two
        case .Two:
            return .Three
        case .Three:
            return .Four
        case .Four:
            return .EndIndex

        /* 
        Add all additional cases above
        */
        case .EndIndex:
            return .EndIndex
        }
    }

    static var allValues: [String] {
        var array: [String] = Array()
        var number = Number.One

        while number != Number.EndIndex {
            array.append(number.rawValue)
            number = number.nextCase()
        }
        return array
    }
}

Pour itérer:

for item in Number.allValues {
    print("number is: \(item)")
}
2
Abbey Jackson

Cela ressemble à un hack, mais si vous utilisez des valeurs brutes, vous pouvez faire quelque chose comme ceci.

enum Suit: Int {  
    case Spades = 0, Hearts, Diamonds, Clubs  
 ...  
}  

var suitIndex = 0  
while var suit = Suit.fromRaw(suitIndex++) {  
   ...  
}  
2
KenH

Voici une méthode que j'utilise pour itérer une énumération et fournir plusieurs types de valeurs d'une énumération

enum IterateEnum: Int {
    case Zero
    case One
    case Two
    case Three
    case Four
    case Five
    case Six
    case Seven

    //Tuple allows multiple values to be derived from the enum case, and
    //since it is using a switch with no default, if a new case is added,
    //a compiler error will be returned if it doesn't have a value Tuple set
    var value: (french:String, spanish:String, japanese:String) {
        switch self {
        case .Zero: return (french:"zéro", spanish:"cero", japanese:"nuru")
        case .One: return (french:"un", spanish:"uno", japanese:"ichi")
        case .Two: return (french:"deux", spanish:"dos", japanese:"ni")
        case .Three: return (french:"trois", spanish:"tres", japanese:"san")
        case .Four: return (french:"quatre", spanish:"cuatro", japanese:"shi")
        case .Five: return (french:"cinq", spanish:"cinco", japanese:"go")
        case .Six: return (french:"six", spanish:"seis", japanese:"roku")
        case .Seven: return (french:"sept", spanish:"siete", japanese:"shichi")
        }
    }

    //Used to iterate enum or otherwise access enum case by index order.
    //Iterate by looping until it returns nil
    static func item(index:Int) -> IterateEnum? {
        return IterateEnum.init(rawValue: index)
    }

    static func numberFromSpanish(number:String) -> IterateEnum? {
        return findItem { $0.value.spanish == number }
    }

    //use block to test value property to retrieve the enum case        
    static func findItem(predicate:((_:IterateEnum)->Bool)) -> IterateEnum? {

        var enumIndex:Int = -1
        var enumCase:IterateEnum?

        //Iterate until item returns nil
        repeat {
            enumIndex += 1
            enumCase = IterateEnum.item(index: enumIndex)

            if let eCase = enumCase {

                if predicate(eCase) {
                    return eCase
                }
            }
        } while enumCase != nil
        return nil
    }
}

var enumIndex:Int = -1
var enumCase:IterateEnum?

//Iterate until item returns nil
repeat {
    enumIndex += 1
    enumCase = IterateEnum.item(index: enumIndex)
    if let eCase = enumCase {
        print("The number \(eCase) in french: \(eCase.value.french), spanish: \(eCase.value.spanish), japanese: \(eCase.value.japanese)")
    }
} while enumCase != nil

print("Total of \(enumIndex) cases")

let number = IterateEnum.numberFromSpanish(number: "siete")

print("siete in japanese: \((number?.value.japanese ?? "Unknown"))")

C'est la sortie:

Le nombre zéro en français: zéro, espagnol: cero, japonais: nuru
Le numéro un en français: un, espagnol: uno, japonais: ichi
Le nombre Deux en français: deux, espagnol: dos, japonais: ni
Le nombre Trois en français: trois, espagnol: tres, japonais: san
Le nombre Quatre en français: quatre, espagnol: cuatro, japonais: shi
Le nombre Cinq en français: cinq, espagnol: cinco, japonais: go
Le nombre Six en français: six, espagnol: seis, japonais: roku
Le nombre Sept en français: septembre, espagnol: siete, japonais: shichi

Total de 8 cas

siete en japonais: shichi


METTRE &AGRAVE; JOUR

J'ai récemment créé un protocole pour gérer l'énumération. Le protocole nécessite une énumération avec une valeur brute Int:

protocol EnumIteration {

    //Used to iterate enum or otherwise access enum case by index order. Iterate by looping until it returns nil

    static func item(index:Int) -> Self?
    static func iterate(item:((index:Int, enumCase:Self)->()), completion:(()->())?) {
    static func findItem(predicate:((enumCase:Self)->Bool)) -> Self?
    static func count() -> Int
}

extension EnumIteration where Self: RawRepresentable, Self.RawValue == Int {

    //Used to iterate enum or otherwise access enum case by index order. Iterate by looping until it returns nil
    static func item(index:Int) -> Self? {
        return Self.init(rawValue: index)
    }

    static func iterate(item:((index:Int, enumCase:Self)->()), completion:(()->())?) {

        var enumIndex:Int = -1
        var enumCase:Self?

        //Iterate until item returns nil
        repeat {
            enumIndex += 1
            enumCase = Self.item(enumIndex)

            if let eCase = enumCase {
                item(index: enumIndex, enumCase: eCase)
            }
        } while enumCase != nil
        completion?()
    }

    static func findItem(predicate:((enumCase:Self)->Bool)) -> Self? {

        var enumIndex:Int = -1
        var enumCase:Self?

        //Iterate until item returns nil
        repeat {
            enumIndex += 1
            enumCase = Self.item(enumIndex)

            if let eCase = enumCase {

                if predicate(enumCase:eCase) {
                    return eCase
                }
            }
        } while enumCase != nil
        return nil
    }

    static func count() -> Int {
        var enumIndex:Int = -1
        var enumCase:Self?

        //Iterate until item returns nil
        repeat {
            enumIndex += 1
            enumCase = Self.item(enumIndex)
        } while enumCase != nil

        //last enumIndex (when enumCase == nil) is equal to the enum count
        return enumIndex
    }
}
2
MSimic

Voici mon approche suggérée. Ce n'est pas complètement satisfaisant (je suis très novice dans Swift et OOP!) Mais peut-être que quelqu'un peut l'affiner. L'idée est de faire en sorte que chaque énumération fournisse ses propres informations de plage sous forme de propriétés .first et .last. Il ajoute juste deux lignes de code à chaque énumération: toujours un peu codé en dur, mais au moins, il ne duplique pas l’ensemble. Cela nécessite de modifier le Suit enum pour qu'il soit un Int comme le Rank est enum, au lieu de non typé. 

Plutôt que de faire écho à l'ensemble de la solution, voici le code que j'ai ajouté à Rank enum, quelque part après les instructions case (Suit enum est similaire):

var first: Int { return Ace.toRaw() }
var last: Int { return King.toRaw() }

et la boucle que j'ai utilisée pour construire le deck sous la forme d'un tableau de String. (La définition du problème ne précisait pas comment le jeu devait être structuré.)

func createDeck() -> [String] {
var deck: [String] = []
var card: String
for r in Rank.Ace.first...Rank.Ace.last {
    for s in Suit.Hearts.first...Suit.Hearts.last {
       card = Rank.simpleDescription( Rank.fromRaw(r)!)() + " of " + Suit.simpleDescription( Suit.fromRaw(s)!)()
       deck.append( card)
       }
   }
return deck
}

Ce n'est pas satisfaisant car les propriétés sont associées à un élément plutôt qu'à l'énum. Mais cela ajoute de la clarté aux boucles "pour". J'aimerais qu'il dise Rank.first au lieu de Rank.Ace.first. Cela fonctionne (avec n'importe quel élément), mais c'est moche. Quelqu'un peut-il montrer comment élever cela au niveau enum?

Et pour que cela fonctionne, j'ai sorti la méthode createDeck de la structure Card ... Je ne pouvais pas trouver comment obtenir un tableau [String] renvoyé de cette structure, et cela semble de toute façon un mauvais endroit pour mettre une telle méthode.

2
Roger Worden

Une autre solution: 

enum Suit: String {
    case spades = "♠"
    case hearts = "♥"
    case diamonds = "♦"
    case clubs = "♣"

    static var count: Int {
        return 4   
    }

    init(index: Int) {
        switch index {
            case 0: self = .spades
            case 1: self = .hearts
            case 2: self = .diamonds
            default: self = .clubs
        }
    }
}

for i in 0..<Suit.count {
    print(Suit(index: i).rawValue)
}
2
Miguel Gallego
enum Rank: Int
{
    case Ace = 0
    case Two, Three, Four, Five, Six, Seve, Eight, Nine, Ten
    case Jack, Queen, King
    case Count
}

enum Suit : Int
{
    case Spades = 0
    case Hearts, Diamonds, Clubs
    case Count
}

struct Card
{
    var rank:Rank
    var suit:Suit
}

class Test
{
    func makeDeck() -> Card[]
    {
        let suitsCount:Int = Suit.Count.toRaw()
        let rankCount:Int = Rank.Count.toRaw()
        let repeatedCard:Card = Card(rank:Rank.Ace, suit:Suit.Spades)
        let deck:Card[] = Card[](count:suitsCount*rankCount, repeatedValue:repeatedCard)

        for i:Int in 0..rankCount
        {
            for j:Int in 0..suitsCount
            {
                deck[i*suitsCount+j] = Card(rank: Rank.fromRaw(i)!, suit: Suit.fromRaw(j)!)
            }
        }
        return deck
    }
}

Basé sur la réponse de Rick: c'est 5 fois plus rapide

1

(Amélioration de la réponse de Karthik Kumar)

Cette solution utilise le compilateur pour vous garantir de ne manquer aucun cas.

enum Suit: String {
    case spades = "♠"
    case hearts = "♥"
    case diamonds = "♦"
    case clubs = "♣"

    static var enumerate: [Suit] {
        switch Suit.spades {
        // make sure the two lines are identical ^_^
        case        .spades, .hearts, .diamonds, .clubs:
            return [.spades, .hearts, .diamonds, .clubs]
        }
    }
}
1
Cœur

Il existe une méthode intelligente et frustrante, car elle illustre la différence entre les deux types d’énums différents.

Essaye ça:

    func makeDeck() -> Card[] {
      var deck: Card[] = []
      var suits: Suit[] = [.Hearts, .Diamonds, .Clubs, .Spades]
      for i in 1...13 {
        for suit in suits {
          deck += Card(rank: Rank.fromRaw(i)!, suit: suit)
        }
      }
      return deck
    }

L’accord est qu’une énumération adossée à des nombres (valeurs brutes) est explicitement commandée, alors qu’une énumération qui n’est pas adossée à des chiffres est explicitement non ordonnée.

Par exemple. lorsque nous donnons aux nombres des valeurs enum, le langage est assez rusé pour comprendre dans quel ordre se trouvent les nombres ... Si, par contre, nous ne donnons aucun ordre, nous essayons de parcourir les valeurs du langage. lève les mains en l'air et va "oui, mais lequel voulez-vous y aller en premier ???"

Les autres langues pouvant effectuer cette opération (itération sur des énumérations non ordonnées) peuvent être les mêmes où tout est «caché», en réalité une carte ou un dictionnaire, et vous pouvez effectuer une itération sur les clés d’une carte, qu’il y ait ou non un ordre logique.

Le truc est donc de lui fournir quelque chose qui est explicitement commandé, dans ce cas des instances des combinaisons dans un tableau dans l'ordre que nous voulons. Dès que vous donnez cela, Swift est comme "pourquoi vous ne l'avez pas dit en premier lieu?"

L'autre astuce consiste à utiliser l'opérateur de forçage sur la fonction fromRaw. Cela illustre un autre «casse-tête» concernant les énumérations, selon lequel la plage de valeurs possibles à transmettre est souvent plus grande que la plage d'énums. Par exemple, si nous disons Rank.fromRaw (60), aucune valeur ne sera renvoyée. Nous utilisons donc la fonctionnalité optional de la langue, et nous commencerons bientôt à utiliser des options, ce qui sera forcé. (Ou alternativement la construction if let qui me semble encore un peu bizarre)

1
Rick

Solution Swift 5: La solution dans Swift 5 est assez simple:

enum Suit: String, CaseIterable {
    case spades = "♠"
    case hearts = "♥"
    case diamonds = "♦"
    case clubs = "♣"
}

// then access the cases like this:

for suitKey in LocalizationKey.allCases {
    print(suitKey)
}
1
J.D. Wooder

J'ai utilisé la méthode ci-dessous, l'hypothèse est que je sais quelle est la dernière valeur de l'en-tête Rank et que tous les rangs ont des valeurs incrémentielles après l'As

Je préfère cette façon comme c'est propre et petit, facile à comprendre

 func cardDeck() -> Card[] {
    var cards: Card[] = []
    let minRank = Rank.Ace.toRaw()
    let maxRank = Rank.King.toRaw()

    for rank in minRank...maxRank {
        if var convertedRank: Rank = Rank.fromRaw(rank) {
            cards.append(Card(rank: convertedRank, suite: Suite.Clubs))
            cards.append(Card(rank: convertedRank, suite: Suite.Diamonds))
            cards.append(Card(rank: convertedRank, suite: Suite.Hearts))
            cards.append(Card(rank: convertedRank, suite: Suite.Spades))
        }
    }

    return cards
}
1
Ninad Shah

Parfois, vous pouvez traiter un type énuméré avec un type entier brut sous-jacent qui change tout au long du cycle de développement du logiciel. Voici un exemple qui fonctionne bien pour ce cas:

public class MyClassThatLoadsTexturesEtc
{
    //...

    // Colors used for gems and sectors.
    public enum Color: Int
    {
        // Colors arranged in order of the spectrum.
        case First = 0
        case Red, Orange, Yellow, Green, Blue, Purple, Pink
        // --> Add more colors here, between the first and last markers.
        case Last
    }

    //...

    public func preloadGems()
    {
        // Preload all gems.
        for i in (Color.First.toRaw() + 1) ..< (Color.Last.toRaw())
        {
            let color = Color.fromRaw(i)!
            loadColoredTextures(forKey: color)
        }
    }

    //...
}
0
Byron Formwalt

Il m'a fallu un peu plus qu'une seule méthode dans la structure, comme le récit le livre Swift, mais j'ai configuré les fonctions suivantes dans l'énum. J'aurais utilisé un protocole mais je ne suis pas sûr de savoir pourquoi mais le fait que le rang soit défini comme int le perturbe

enum Rank: Int {
    case Ace = 1
    case Two, Three, Four, Five, Six, Seve, Eight, Nine, Ten
    case Jack, Queen, King
    func simpleDescription() -> String {
        switch self{
        case .Ace:
            return "ace"
        case .Jack:
            return "jack"
        case .Queen:
            return "Queen"
        case .King:
            return "King"
        default:
            return String(self.toRaw())
        }
    }
    mutating func next() -> Rank {
        var rank = self
        var rawrank = rank.toRaw()
        var nrank:Rank = self
        rawrank = rawrank + 1
        if let newRank = Rank.fromRaw(rawrank) {
            println("\(newRank.simpleDescription())")
            nrank = newRank
        } else {
            return self
        }
        return nrank
    }
}

enum Suit {
    case Spades, Hearts, Diamonds, Clubs
    func color() -> String {
        switch self{
        case .Spades, .Clubs:
            return "black"
        default:
            return "red"
        }
    }
    func simpleDescription() -> String {
        switch self{
        case .Spades:
            return "spades"
        case .Hearts:
            return "hearts"
        case .Diamonds:
            return "diamonds"
        case .Clubs:
            return "clubs"
        }
    }
    mutating func next() -> Suit {
        switch self{
        case .Spades:
            return Hearts
        case .Hearts:
            return Diamonds
        case .Diamonds:
            return Clubs
        case .Clubs:
            return Spades
        }
    }
}

struct Card {
    var rank:Rank
    var suit:Suit
    func deck() -> Card[] {
        var tRank = self.rank
        var tSuit = self.suit
        let tcards = 52 // we start from 0
        var cards: Card[] = []
        for i in 0..tcards{
            var card = Card(rank: tRank, suit: tSuit)
            cards.append(card)
            tRank = tRank.next()
            tSuit = tSuit.next()
        }
        return cards
    }
    func simpleDescription() -> String {
        return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
    }
}

var card = Card(rank: .Ace, suit: .Spades)
var deck = card.deck()

j'espère que cela aide en gros, j'ai utilisé un peu de connaissances générales, mais cela peut être facilement corrigé en multipliant les suites par rang Note différente pour gagner du temps, j'ai utilisé des valeurs brutes de rangs. Vous pouvez faire la même chose pour les costumes si vous le souhaitez, mais l'exemple ne le contient pas.

0
Snymax

J'ai ajouté la fonction count (), et itère les valeurs:

public enum MetricType : Int {
case mvps = 0
case allNBA = 1
case championshipRings = 2
case finalAppearances = 3
case gamesPlayed = 4
case ppg = 5

static func count() -> Int {
    return (ppg.rawValue) + 1
}

static var allValues: [MetricType] {
    var array: [MetricType] = Array()
    var item : MetricType = MetricType.mvps
    while item.rawValue < MetricType.count() {
        array.append(item)
        item = MetricType(rawValue: (item.rawValue + 1))!
    }
    return array
}

}

0
Mihaela

Ma solution est de déclarer un tableau avec toutes les possibilités enum, afin de pouvoir les parcourir toutes.

//Function inside struct Card
static func generateFullDeck() -> [Card] {
    let allRanks = [Rank.Ace, Rank.Two, Rank.Three, Rank.Four, Rank.Five, Rank.Six, Rank.Seven, Rank.Eight, Rank.Nine, Rank.Ten, Rank.Jack, Rank.Queen, Rank.King]
    let allSuits = [Suit.Hearts, Suit.Diamonds, Suit.Clubs, Suit.Spades]
    var myFullDeck: [Card] = []

    for myRank in allRanks {
        for mySuit in allSuits {
            myFullDeck.append(Card(rank: myRank, suit: mySuit))
        }
    }
    return myFullDeck
}

//actual use:
let aFullDeck = Card.generateFullDeck()    //Generate the desired full deck

var allDesc: [String] = []
for aCard in aFullDeck {
    println(aCard.simpleDescription())    //You'll see all the results in playground
}
0
Jeff Chen

J'ai trouvé un moyen un peu hacky, mais beaucoup plus sûr de le faire, qui ne nécessite pas de saisir les valeurs deux fois ou de référencer la mémoire des valeurs enum, ce qui rend très peu probable la rupture.

Fondamentalement, au lieu d'utiliser une énumération, créez une structure avec une seule instance et créez toutes les constantes de valeurs énumérées. Les variables peuvent ensuite être interrogées à l’aide de Mirror

public struct Suit{

    // the values
    let spades = "♠"
    let hearts = "♥"
    let diamonds = "♦"
    let clubs = "♣"

    // make a single instance of the Suit struct, Suit.instance
    struct SStruct{static var instance: Suit = Suit()}
    static var instance : Suit{
        get{return SStruct.instance}
        set{SStruct.instance = newValue}
    }

    // an array with all of the raw values
    static var allValues: [String]{
        var values = [String]()

        let mirror = Mirror(reflecting: Suit.instance)
        for (_, v) in mirror.children{
            guard let suit = v as? String else{continue}
            values.append(suit)
        }

        return values
    }
}

Si vous utilisez cette méthode, pour obtenir une valeur unique, vous devez utiliser Suit.instance.clubs ou Suit.instance.spades.

Mais tout cela est si ennuyeux ... Faisons des choses qui font que cela ressemble davantage à une véritable énorme!

public struct SuitType{

    // store multiple things for each suit
    let spades = Suit("♠", order: 4)
    let hearts = Suit("♥", order: 3)
    let diamonds = Suit("♦", order: 2)
    let clubs = Suit("♣", order: 1)

    struct SStruct{static var instance: SuitType = SuitType()}
    static var instance : SuitType{
        get{return SStruct.instance}
        set{SStruct.instance = newValue}
    }

    // a dictionary mapping the raw values to the values
    static var allValuesDictionary: [String : Suit]{
        var values = [String : Suit]()

        let mirror = Mirror(reflecting: SuitType.instance)
        for (_, v) in mirror.children{
            guard let suit = v as? Suit else{continue}
            values[suit.rawValue] = suit
        }

        return values
    }
}

public struct Suit: RawRepresentable, Hashable{
    public var rawValue: String
    public typealias RawValue = String

    public var hashValue: Int{
        // find some integer that can be used to uniquely identify
        // each value. In this case, we could have used the order
        // variable because it is a unique value, yet to make this
        // apply to more cases, the hash table address of rawValue
        // will be returned, which should work in almost all cases
        // 
        // you could also add a hashValue parameter to init() and
        // give each suit a different hash value
        return rawValue.hash
    }

    public var order: Int
    public init(_ value: String, order: Int){
        self.rawValue = value
        self.order = order
    }

    // an array of all of the Suit values
    static var allValues: [Suit]{
        var values = [Suit]()

        let mirror = Mirror(reflecting: SuitType.instance)
        for (_, v) in mirror.children{
            guard let suit = v as? Suit else{continue}
            values.append(suit)
        }

        return values
    }

    // allows for using Suit(rawValue: "♦"), like a normal enum
    public init?(rawValue: String){
        // get the Suit from allValuesDictionary in SuitType, or return nil if that raw value doesn't exist
        guard let suit = SuitType.allValuesDictionary[rawValue] else{return nil}
        // initialize a new Suit with the same properties as that with the same raw value
        self.init(suit.rawValue, order: suit.order)
    }
}

Vous pouvez maintenant faire des choses comme

let allSuits: [Suit] = Suit.allValues

ou

for suit in Suit.allValues{
   print("The suit \(suit.rawValue) has the order \(suit.order)")
}

Cependant, pour en obtenir un, vous devez toujours utiliser SuitType.instance.spades ou SuitType.instance.hearts. Pour rendre cela un peu plus intuitif, vous pouvez ajouter du code à Suit qui vous permet d’utiliser Suit.type.* au lieu de SuitType.instance.*

public struct Suit: RawRepresentable, Hashable{
   // ...your code...

   static var type = SuitType.instance

   // ...more of your code...
}

Vous pouvez maintenant utiliser Suit.type.diamonds au lieu de SuitType.instance.diamonds ou Suit.type.clubs au lieu de SuitType.instance.clubs

0
Jojodmo

Adapter la réponse de @ rintaro à Swift 3 dans laquelle rintaro:

  • a écrit une fonction utilitaire iterateEnum() pour itérer des cas pour des types enum arbitraires
  • mis en œuvre des idées de casting de @ réponse de Kametrixom

Swift3

func iterateEnum<T: Hashable>(_: T.Type) -> AnyGenerator<T> {
    var i = 0
    return AnyGenerator {
        let next = withUnsafePointer(&i) { UnsafePointer<T>($0).memory }

        let value : T?
        if next.hashValue == i {
            value = next
        } else {
            value = nil
        }
        i = i + 1
        return value
    }
}
0
yo.ian.g