¿Cómo enumerar una enumeración con el tipo de cadena?

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

Por ejemplo, ¿cómo puedo hacer algo como:

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

Ejemplo resultante:

♠
♥
♦
♣
Lucien
fuente
¿En qué caso no sabrías el tipo?
mtbennett
Tienes razón, en este caso es del tipo String.
Lucien
Todavía no hay reflejo en Swift ...
Sulthan
22
¿No es irónico que se les llame enumeraciones, pero son tan dolorosamente molestas de enumerar en Swift?
Charlton Provatas
3
@CharltonProvatas Si ese fuera el único inconveniente en Swift, lo llamaría un día. Mirando cuántas personas ofrecen diferentes soluciones para esto, solo estoy royendo mi teclado.
qwerty_so

Respuestas:

267

Swift 4.2+

Comenzando con Swift 4.2 (con Xcode 10), solo agregue conformidad de protocolo CaseIterablepara beneficiarse allCases. Para agregar esta conformidad de protocolo, simplemente necesita escribir en alguna parte:

extension Suit: CaseIterable {}

Si la enumeración es suya, puede especificar la conformidad directamente en la declaración:

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

Luego, el siguiente código imprimirá todos los valores posibles:

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

Compatibilidad con versiones anteriores de Swift (3.xy 4.x)

Si necesita admitir Swift 3.xo 4.0, puede imitar la implementación de Swift 4.2 agregando el siguiente código:

#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
Coeur
fuente
@DmitryPetukhov Estaré encantado de ayudar, pero: (1) ¿estás seguro de que tienes la última versión del código? (se solucionó un bloqueo hace un mes) y (2) proporcione un MCVE de su tipo personalizado que pueda reproducir un bloqueo y su versión de Xcode.
Coeur
Esto funciona bien para mí para las compilaciones de depuración, pero tan pronto como creo una versión y la subo a TestFlight se bloquea. ¿Apple está de alguna manera quitando esto?
Daniel Wood
1
Parece que su versión tiene un positivo sobre la versión incorporada de CaseIterator. Puedo ampliar las enumeraciones que se definen en otro archivo con su versión. Si hace que allCases en la extensión sea pública, también puede extender las enumeraciones definidas en diferentes marcos.
adamfowlerphoto
1
@CyberMew He actualizado la respuesta para aclararla. El libro de Swift y las notas de la versión Xcode 10 de CaseIterable son posteriores a mi respuesta y utilizaron ejemplos simplificados en los que la enumeración no está respaldada String, a diferencia de la pregunta actual de desbordamiento de pila.
Coeur
1
Me gustaría enfatizar la importancia de "# if! Swift (> = 4.2)". Si escribió su código antes de swift 4.2 y olvidó eliminar el siguiente código "# if! Swift (> = 4.2)", cuando use Xcode Versión 11.4 para construir localmente en su dispositivo de prueba, todo estará bien. Pero cuando su aplicación se descarga de la tienda de aplicaciones o el vuelo de prueba, ese código bloqueará su aplicación. Ese tipo de error es muy difícil de detectar o depurar.
Oliver Zhang
524

Esta publicación es relevante aquí https://www.swift-studies.com/blog/2014/6/10/enumerating-enums-in-swift

Esencialmente, la solución propuesta es

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

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

for category in ProductCategory.allValues{
     //Do something
}
rougeExciter
fuente
188
Bien, pero ... tienes que ingresar tus elementos de la enumeración dos veces, una para la enumeración, una vez para los valores. No es tan elegante como uno desearía.
Jay Imerman
2
De acuerdo con el "pero" ... sin embargo, como se indica en el artículo, tal vez haya un problema de que una enumeración es realmente un conjunto y, por lo tanto, desordenada ... eso sí ... ¡los casos de orden definidos en no serían un mal comienzo!
rougeExciter
3
En Java, el compilador hace esto por usted, tal vez Swift 2.0 también lo haga. En particular, en Java, todas las enumeraciones obtienen un método de descripción (toString en Java) que le da a String los nombres de los casos (Washers, ...) y se crea automáticamente un Conjunto de los casos. Java también te ofrece indexación posicional. Como dije, tal vez Swift 2.0.
Howard Lovatt
1
Idealmente, tendría algo similar a la implementación de c # en la que puede hacerlo, Enum.Values(typeof(FooEnum))pero expuesto como un método de extensión (como map o reduce). FooEnum.values() :: values(EnumType -> [EnumType])
rodrigoelp
1
El artículo hace un buen punto al final acerca de hacer que cada valor de enumeración esté en la matriz allValues ​​con una prueba unitaria. Sin embargo, todavía puedo ver a alguien agregando más elementos, pero no imponiéndolos en la prueba de la unidad que todavía nos deja al principio, sin saber con certeza que todos los valores de enumeración se mantienen en todos los Valores.
DonnaLea
278

Hice una función de utilidad iterateEnum()para iterar casos para enumtipos arbitrarios .

Aquí está el uso de ejemplo:

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

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

Qué salidas:

♠
♥
♦
♣

Pero, esto es solo para propósitos de depuración o prueba : esto se basa en varios comportamientos del compilador Swift1.1 no documentados, por lo tanto, úselo bajo su propio riesgo.

Aquí está el código:

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

La idea subyacente es:

  • La representación en memoria de enum, excluyendo enums con tipos asociados, es solo un índice de casos cuando el recuento de los casos es 2...256, es idéntico a UInt8, cuándo 257...65536, es, UInt16etc. Por lo tanto, puede ser unsafeBitcastde tipos enteros sin signo correspondientes.
  • .hashValue de los valores de enumeración es el mismo que el índice del caso.
  • .hashValuede valores de enumeración bitcasted de índice inválido es 0.

Revisado para Swift2 e implementado ideas de casting de la respuesta de @ Kametrixom :

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

Revisado para 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
    }
}

Revisado para 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
    }
}
rintaro
fuente
20
¡Impresionante y la única respuesta que responde a la pregunta! Pero sí ... ¡no lo tocaré! ¡+1 por esfuerzo, sin embargo!
dotToString
Acabo de publicar mi respuesta que funciona básicamente de la misma manera (solo vi esta respuesta más tarde). Utiliza Swift 2.0 beta 6 y las características modernas del lenguaje.
Kametrixom
2
La versión Swift 3 funciona bien. Solo necesitaba modificar un poco el uso: para f en iterateEnum (Suit.self) {print (f.rawValue)}
Gene De Lisa
16
+1 esto es bastante brillante. También, en mi humilde opinión, es demasiado inteligente para usar, como lo demuestra su ruptura significativa en cada cambio importante de versión de Swift. Para crédito del autor, la versión de Swift 3 se realizó un mes antes de que Swift 3 saliera de la versión beta ... Si vas a tomar esta respuesta y aprender todo esto withUnsafePointer withMemoryReboundy otras pointeecosas, entonces úsala por todos los medios. De lo contrario, lo evitaría.
Dan Rosenstark
55
Solo quiero agregar que esto ahora está roto en swift 4, pero solo en linux, por lo que +1 a los comentarios anteriores es demasiado inteligente para usar.
Andrew Plummer
130

Las otras soluciones funcionan, pero todas suponen, por ejemplo, el número de posibles rangos y trajes, o cuál puede ser el primer y el último rango. Es cierto que el diseño de una baraja de cartas probablemente no va a cambiar mucho en el futuro previsible. En general, sin embargo, es mejor escribir código que haga la menor cantidad posible de suposiciones. Mi solución:

He agregado un tipo sin formato a la Suitenumeración, por lo que puedo usar Suit(rawValue:)para acceder a los Suitcasos:

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

Debajo de la implementación del createDeck()método de la Tarjeta . init(rawValue:)es un inicializador disponible y devuelve un opcional. Por desenvolver y la comprobación de su valor en los dos estados mientras, no hay necesidad de asumir el número de Ranko Suitcasos:

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

Aquí es cómo llamar al createDeckmétodo:

let card = Card(rank: Rank.Ace, suit: Suit.Clubs)
let deck = card.createDeck()
sdduursma
fuente
12
La MEJOR respuesta absoluta que he visto en varios hilos sobre este tema. Muy elegante. Esto funciona con enumeraciones de tipo Int, pero me pregunto cómo se podría iterar a través de otros tipos (cadena, tipos personalizados, etc.).
Jay Imerman
3
Esta es definitivamente la mejor solución. Una cosa a tener en cuenta. En el ejemplo en el libro no tiene "case Spades = 1" como sdduursma tiene. No entendí esto al principio. esa es una opción, o simplemente puede usar "var m = 0"
Joshua Goossen
10
Esto supone que los valores brutos son secuenciales. Cuando esto no es cierto, por ejemplo, cuando la enumeración representa banderas de máscara de bits, el bucle se cierra prematuramente.
Jim
1
Esta solución supone que puede modificar la definición de Suit. En este ejemplo, puede hacerlo, pero el ejercicio estaba destinado a ayudarlo a trabajar con los enumsque recibió como si fueran de una fuente externa.
Josh J
1
Lo único de lo que quiero quejarme es que no puedo llamarlo como un método estático, sino que primero tengo que crear un objeto de tarjeta.
qed
76

Tropecé con los bits y bytes y creé una extensión que luego descubrí que funciona de manera muy similar a la respuesta de @rintaro . Se usa así:

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

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

Lo notable es que se puede usar en cualquier enumeración sin valores asociados. Tenga en cuenta que esto no funciona para las enumeraciones que no tienen casos.

Al igual que con la respuesta de @rintaro , este código usa la representación subyacente de una enumeración. Esta representación no está documentada y podría cambiar en el futuro, lo que la rompería. No recomiendo el uso de esto en producción.

Código (Swift 2.2, Xcode 7.3.1, no funciona en 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
            }
        }
    }
}

Código (Swift 3, Xcode 8.1, no funciona en 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
            }
        }
    }
}

No tengo idea de por qué lo necesito typealias, pero el compilador se queja sin él.

Kametrixom
fuente
10
Esta respuesta es incluso mejor que mi respuesta, especialmente en la parte del casting :)
rintaro
¿Pero creo que esto funciona solo en un pequeño entorno endian?
rintaro
55
¡Xcode 8 beta 6 ha cambiado esto nuevamente! Recibo el siguiente error '' init 'no está disponible: use' withMemoryRebound (to: capacity: _) 'para ver temporalmente la memoria como otro tipo compatible con el diseño'.
Vorlon confuso
1
@ConfusedVorlon: vea la respuesta anterior por @Rintaro: reemplace withUnsafePointer... pointee}porwithUnsafePointer(to: &i) { $0.withMemoryRebound(to: T.self, capacity: 1) { $0.pointee } }
Stefan
1
Parece que ya no funciona a partir de Xcode 10. (Sé que esto no sería necesario con Swift 4.2) pero cuando se usa Swift 4 (.1) en Xcode 10 este código ya no funciona (el valor bruto es desigual)
benrudhart
26

Puede iterar a través de una enumeración mediante la implementación del ForwardIndexTypeprotocolo.

El ForwardIndexTypeprotocolo requiere que defina una successor()función para recorrer los elementos.

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

Iterar sobre un rango abierto o cerrado ( ..<o ...) llamará internamente a la successor()función que le permite escribir esto:

// 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
}
RndmTsk
fuente
2
Considero que esta es la respuesta más "adecuada" a la pregunta, incluso la más "elegante" (código adicional necesario frente a otras opciones aquí soportadas) dada la sintaxis resultante cuando se usa dentro de un rango (la sintaxis es lo que yo esperaría poder hacerlo si las enumeraciones fueran enumerables sin soluciones). ¡Gracias! Aunque vale la pena señalar que si la sobrecarga del operador se emplea en otro lugar además del sucesor () (lo que parece tentador), entonces obviamente el desenvolvimiento forzado es peligroso. Además, el infijo parece innecesario ...?
iOS Gamer
Respuesta actualizada para reflejar las últimas especificaciones del lenguaje Swift
RndmTsk
Un successor()método correctamente definido (primera opción) eliminaría la necesidad de enumtener un tipo asociado. +1
nhgrif
1
Pero esta elegante respuesta no funcionará para las enumeraciones de String, ¿verdad?
Ali
¡La solución más "adecuada" / mejor práctica! + 1-ed
Siu Ching Pong -Asuka Kenji-
18

Este problema ahora es mucho más fácil. Aquí está mi solución 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:

Me gusta esta solución que armé después de encontrar " Comprensión de listas en Swift ".

Utiliza Int raws en lugar de Strings pero evita escribir dos veces, permite personalizar los rangos y no codifica los valores brutos.

Esta es una versión Swift 4 de mi solución original, pero vea la mejora 4.2 anterior:

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
}
adazacom
fuente
Oh, ahora veo que el mío es básicamente el mismo que el de Sutean Rutjanalard.
adazacom
1
En realidad, me gustó más tu implementación. ¡Creo que está más claro! 1 voto a favor. En realidad, las respuestas más votadas son demasiado inteligentes y seguramente se romperán en el futuro. La suya promete cierta estabilidad en el futuro.
jvarela
17

En principio, es posible hacerlo de esta manera, suponiendo que no utilice la asignación de valores sin procesar para los casos de 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())")
}
Alfa07
fuente
77
Esto es bueno, pero solo funciona para enumeraciones enteras continuas a partir de 0
Robert
@Robert, como dice mi comentario más arriba: "no se utiliza la asignación de valores sin procesar para los casos de enum"
Alfa07
Sí, no use valores sin formato, ni configure el tipo subyacente como int. En Swift no necesitas un tipo para una enumeración como en el ejemplo de trajes. enum ItWontWorkForThisEnum {case a, b, c}
Robert
¿Cómo abordaría esto el problema si una tupla está asociada con el caso de enumeración?
rougeExciter
No se puede asociar una tupla a una enumeración muy fácilmente.
nhgrif
13

Si le da a la enumeración un valor Int sin procesar , hará que el bucle sea mucho más fácil.

Por ejemplo, puede usar anyGeneratorpara obtener un generador que pueda enumerar sus valores:

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

Sin embargo, esto parece un patrón bastante común, ¿no sería bueno si pudiéramos hacer enumerable cualquier tipo de enumeración simplemente cumpliendo con un protocolo? Bueno, con Swift 2.0 y extensiones de protocolo, ¡ahora podemos!

Simplemente agregue esto a su proyecto:

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

Ahora, cada vez que cree una enumeración (siempre que tenga un valor bruto Int), puede hacerla enumerable conforme al protocolo:

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

Si sus valores de enumeración no comienzan con 0(el valor predeterminado), anule el firstRawValuemétodo:

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

La clase final de Traje, que incluye el reemplazo simpleDescriptioncon el protocolo CustomStringConvertible más estándar , se verá así:

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

Sintaxis de Swift 3:

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
    }
}
Sensato
fuente
nextIndex ++ se eliminará en swift 3. ¿Qué sugiere como reemplazo de? var nextIndex = firstRawValue () return anyGenerator {Self (rawValue: nextIndex ++)}
Avi
Lo averigué. aplazar {nextIndex + = 1} return AnyGenerator {Self (rawValue: nextIndex)}
Avi
12

Actualizado a 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
        }
    }
}

Es el código actualizado a la respuesta de Swift 2.2 form @ Kametrixom

Para Swift 3.0+ (muchas gracias a @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
        }
    }
}
ale_stro
fuente
@silvansky, ¿podría explicar a qué se refiere?
ale_stro
Vaya, lo siento, volví a comprobar y había un error en el patio de juegos: en el proyecto real, este código funciona como se esperaba, ¡gracias! =)
silvansky
1
Gran solución! Además, funciona bien en Swift 3 con cambios mínimos ('AnyGenerator' renombró 'AnyIterator' y '.memory' renombró a '.pointee').
Philip
9

Solución Swift 5:

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

// access cases like this:

for suitKey in Suit.allCases {
    print(suitKey)
}
JD Wooder
fuente
7

Me encontré haciendo .allValuesmucho en todo mi código. Finalmente descubrí una manera de simplemente conformarme a un Iteratableprotocolo y tener un rawValues()método.

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"]
Fabian Buentello
fuente
7

Xcode 10 con 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 }
}

Llámalo

print(Filter.allValues)

Huellas dactilares:

["Salario", "Experiencia", "Tecnología", "No utilizado", "Alto valor no utilizado"]


versiones anteriores

Por enumrepresentarInt

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

Llámalo así:

print(Filter.allValues)

Huellas dactilares:

[0, 1, 2, 3, 4]


Por enumrepresentarString

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

Llámalo

print(Filter.allValues)

Huellas dactilares:

["Salario", "Experiencia", "Tecnología", "No utilizado", "Alto valor no utilizado"]

Warif Akhand Rishi
fuente
7

EDITAR: Propuesta de Evolución Rápida SE-0194 La Colección Derivada de Casos Enum propone una solución nivelada para este problema. Lo vemos en Swift 4.2 y versiones posteriores. La propuesta también señala algunas soluciones alternativas que son similares a algunas ya mencionadas aquí, pero podría ser interesante ver de todos modos.

También mantendré mi publicación original por completo.


Este es otro enfoque basado en la respuesta de @ Peymmankh, adaptado a 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)
}
ff10
fuente
5
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
    }
}

¿Qué tal esto?

Mossila
fuente
Gracias, funciona como lo necesito. Pero, ¿hay alguna oportunidad en el cierre del mapa para obtener el valor no por índice sino por nombre?
Mikhail
4

Puedes intentar enumerar así

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]
Karthik Kumar
fuente
1
¡Eso es mucho código inútil! Es equivalente a static var enumerate = [Mercury, Venus, Earth, Mars], por lo que es una respuesta deficiente en comparación con la respuesta más votada stackoverflow.com/a/24137319/1033581
Cœur
@ Cœur esta respuesta tiene el importante beneficio de usar el compilador para garantizar que no se perderá un caso.
dchakarov
@ Cœur que tiene el mismo problema de permitirle cometer un error de usuario, es decir, el compilador no se quejará si escribe en return [Mercury, Venus, Mars]lugar dereturn [Mercury, Venus, Earth, Mars]
dchakarov
@dchakarov Decidí publicar la mejora como respuesta, para mayor claridad: stackoverflow.com/a/50409525/1033581
Cœur
@ Cœur Si en su nueva respuesta reemplaza la declaración de retorno con esto, return [.spades, .hearts, .clubs]el compilador no dirá nada y luego, cuando intente usarlo en código, obtendrá [TestApp.Suit.spades, TestApp.Suit.hearts, TestApp.Suit.clubs], ese fue mi punto, que si está tratando con un gran enumeración y tiene que agregar o eliminar casos de vez en cuando, su solución es propensa a errores de omisión, mientras que la respuesta actual, aunque no es concisa, es más segura.
dchakarov
4

En Swift 3, cuando tiene la enumeración subyacente rawValue, puede implementar el Strideableprotocolo. Las ventajas son que no se crean matrices de valores como en algunas otras sugerencias y que el bucle Swift estándar "for in" funciona, lo que hace una buena sintaxis.

// "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)")
}
ACK
fuente
Ahh, justo lo que estaba buscando para reemplazar ForwardIndexType. Ahora mis iteraciones se ven bien en el sitio de uso ... solo la forma Swifty adecuada.
Andrew Duncan
4

Esta solución logra el equilibrio adecuado entre legibilidad y mantenibilidad.

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()
Andrés
fuente
En mi opinión, esta es la mejor solución. Cuando veo un código rápido, principalmente, la legibilidad no es mejor que objc. Pero podría ser, si los programadores prestaran mayor atención a los lectores de su código. Sus seres futuros, por ejemplo :)
Vilém Kurz
4

Lo siento, mi respuesta fue específica de cómo usé esta publicación en lo que tenía que hacer. Para aquellos que se topan con esta pregunta y buscan una forma de encontrar un caso dentro de una enumeración, esta es la forma de hacerlo (novedad en Swift 2):

Editar: minúsculas camelCase ahora es el estándar para los valores de enumeración de Swift 3

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

Para aquellos que se preguntan sobre la enumeración en una enumeración, las respuestas dadas en esta página que incluyen un var / let estático que contiene una matriz de todos los valores de enumeración son correctas. El último código de ejemplo de Apple para tvOS contiene exactamente la misma técnica.

Dicho esto, deberían construir un mecanismo más conveniente en el lenguaje (Apple, ¿estás escuchando?).

Gene Loparco
fuente
3

El experimento fue: EXPERIMENTO

Agregue un método a la Tarjeta que cree una baraja completa de cartas, con una carta de cada combinación de rango y palo.

Entonces, sin modificar o mejorar el código dado que no sea agregar el método (y sin usar cosas que aún no se han enseñado), se me ocurrió esta solución:

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()
Hans-Peter
fuente
3

Aquí hay un método que uso para iterar enumy proporcionar múltiples tipos de valores desde unoenum

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

Esta es la salida:

El número cero en francés: zéro, español: cero, japonés: nuru
El número uno en francés: un, español: uno, japonés: ichi
El número dos en francés: deux, español: dos, japonés: ni
El número tres en francés : trois, español: tres, japonés: san
El número cuatro en francés: quatre, español: cuatro, japonés: shi
El número cinco en francés: cinq, español: cinco, japonés: ir
El número seis en francés: seis, español: seis, japonés: roku
El número siete en francés: sept, español: siete, japonés: shichi

Total de 8 casos

siete en japonés: shichi


ACTUALIZAR

Recientemente creé un protocolo para manejar la enumeración. El protocolo requiere una enumeración con un valor bruto 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
    }
}
MSimic
fuente
2

Esto parece un truco, pero si usa valores sin procesar, puede hacer algo como esto

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

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

Al tratar Swift 2.0aquí es mi sugerencia:

He agregado el tipo sin formato a Suit enum

enum Suit: Int {

entonces:

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
    }
}
Aladin
fuente
2

Al igual que con la respuesta de @Kametrixom aquí , creo que devolver una matriz sería mejor que devolver AnySequence, ya que puede tener acceso a todas las ventajas de Array, como count, etc.

Aquí está la reescritura:

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)
    }
}
Peymankh
fuente
2

Otra solución:

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)
}
Miguel Gallego
fuente
2

Esta es una publicación bastante antigua, de Swift 2.0. Ahora hay algunas soluciones mejores aquí que usan las funciones más nuevas de swift 3.0: iterar a través de una enumeración en Swift 3.0

Y en esta pregunta hay una solución que utiliza una nueva característica de Swift 4.2 (aún no publicado cuando escribo esta edición): ¿Cómo obtengo el recuento de una enumeración Swift?


Hay muchas buenas soluciones en este hilo y otras, sin embargo, algunas de ellas son muy complicadas. Me gusta simplificar lo más posible. Aquí hay una solución que puede o no funcionar para diferentes necesidades, pero creo que funciona bien en la mayoría de los casos:

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

Para iterar:

for item in Number.allValues {
    print("number is: \(item)")
}
Abbey Jackson
fuente
1
Eso parece mucho trabajo específico para la enumeración individual que ha creado; no estoy seguro de que return [Number.One.rawValue, Number.Two.rawValue, ...] no sea más limpio, en este caso .
Scott Austin
Esta es una publicación bastante antigua, de Swift 2.0. Ahora hay algunas mejores soluciones aquí que usan las características más nuevas de swift 3.0: stackoverflow.com/questions/41352594/… Y en esta pregunta hay una solución que usa una nueva característica de (la aún no lanzada mientras escribo esta edición ) Swift 4.2: stackoverflow.com/questions/27094878/…
Abbey Jackson
2

Las enumeraciones tienen toRaw()y fromRaw()métodos. Entonces, si su valor bruto es un Int, puede iterar del primero al último 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 problema es que debe probar los valores opcionales antes de ejecutar el simpleDescriptionmétodo, por lo convertedSuitque primero establecemos nuestro valor y luego establecemos una constante enconvertedSuit.simpleDescription()

Adrian Harris Crowne
fuente
2
La pregunta original era sobre un tipo de cadena enum no Int
cynistersix
2

Aquí está mi enfoque sugerido. No es completamente satisfactorio (¡soy muy nuevo en Swift y OOP!), Pero tal vez alguien pueda refinarlo. La idea es que cada enumeración proporcione su propia información de rango .firsty .lastpropiedades. Agrega solo dos líneas de código a cada enumeración: todavía está un poco codificado, pero al menos no está duplicando todo el conjunto. Requiere modificar la Suitenumeración para que sea un Int como la Rankenumeración es, en lugar de sin tipo.

En lugar de hacer eco de toda la solución, aquí está el código que agregué a la .enumeración, en algún lugar después de las declaraciones de casos (la Suitenumeración es similar):

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

y el bucle que usé para construir el mazo como una matriz de String. (La definición del problema no indicaba cómo se iba a estructurar el mazo).

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
}

No es satisfactorio porque las propiedades están asociadas a un elemento en lugar de a la enumeración. Pero sí agrega claridad a los bucles 'for'. Me gustaría decirlo en Rank.firstlugar de Rank.Ace.first. Funciona (con cualquier elemento), pero es feo. ¿Alguien puede mostrar cómo elevar eso al nivel de enumeración?

Y para que funcione, saqué el createDeckmétodo de la estructura de la Tarjeta. No pude averiguar cómo obtener una matriz [String] devuelta desde esa estructura, y de todos modos parece un mal lugar para poner un método de este tipo.

Roger Worden
fuente
2

Lo hice usando la propiedad calculada, que devuelve la matriz de todos los valores (gracias a esta publicación http://natecook.com/blog/2014/10/loopy-random-enum-ideas/ ). Sin embargo, también usa int raw-values, pero no necesito repetir todos los miembros de la enumeración en una propiedad separada.

ACTUALIZACIÓN Xcode 6.1 cambió un poco la forma de obtener el uso de un miembro enum rawValue, así que arreglé el listado. También se corrigió un pequeño error con un error primero rawValue.

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++)
                }
            }
        )
    }
}
gleb vodovozov
fuente
1
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
    }
}

Basado en la respuesta de Rick: esto es 5 veces más rápido

John Lluch-Zorrilla
fuente
Agregar un Countcaso romperá switchimplementaciones exhaustivas y CaseIterableconformidad.
Cœur