swift - títulos - ¿Cómo enumerar una enumeración con tipo de cadena?
normas para enumerar titulos y subtitulos (30)
La segunda respuesta que realmente funciona.
Así que tropecé en los bits y bytes y creé una extensión (que luego descubrí que funciona muy similar a la respuesta de ). Es utilizable así:
enum E : EnumCollection {
case A, B, C
}
Array(E.cases()) // [A, B, C]
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.
Renuncia
Al igual que con la respuesta de , este código utiliza la representación subyacente de una enumeración. Esta representación no está documentada y podría cambiar en el futuro, lo que podría romperla -> No recomiendo el uso de esta 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é necesito las typealias
, pero el compilador se queja sin eso)
(Hice una gran modificación en esta respuesta, mira ediciones de versiones anteriores)
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:
♠
♥
♦
♣
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", "Valor alto no utilizado"]
versiones anteriores
Para enum
representarInt
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]
Para enum
representarString
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", "Valor alto no utilizado"]
Swift 4.2+
Comenzando con Swift 4.2 (con Xcode 10), solo agregue la conformidad del protocolo a CaseIterable
para beneficiarse de todos los allCases
:
extension Suit: CaseIterable {}
Entonces esto imprimirá todos los valores posibles:
Suit.allCases.forEach {
print($0.rawValue)
}
Compatibilidad con versiones anteriores de Swift (3.xy 4.x)
Solo imita la implementación 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
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
}
}
}
Se actualizó el código para Swift 2.2 form @ Kametrixom es un swer
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
}
}
}
EDIT: Swift Evolution Proposal SE-0194 La colección derivada de casos de enumeración propone una solución nivelada a este problema. Lo vemos en Swift 4.2 y posteriores. La propuesta también señala algunas workarounds similares a las que ya se mencionaron aquí, pero podría ser interesante verlas sin embargo.
También mantendré mi publicación original para completar la información.
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)
}
}
El experimento fue: EXPERIMENTO
Agrega un método a la Tarjeta que crea una baraja completa de tarjetas, con una tarjeta de cada combinación de rango y palo.
Así que sin modificar o mejorar el código dado, aparte de 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()
En Swift 3, cuando la enumeración subyacente tiene {rawValue} s, podría implementar el protocolo {Strideable}. Las ventajas son que no se crean matrices de valores como en algunas otras sugerencias, y que la declaración estándar de Swift "for i in ..." funciona, lo que hace que la sintaxis sea buena.
// "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)")
}
En principio, es posible hacerlo de esta manera suponiendo que no utilice la asignación de valores brutos para los casos de enumeración:
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())")
}
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
}
Esto es con lo que terminé yendo; Creo que encuentra el equilibrio correcto entre legibilidad y facilidad de mantenimiento.
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()
Hice una función de utilidad iterateEnum()
para iterar casos para tipos de enum
arbitrarios.
Aquí está el ejemplo de uso:
enum Suit:String {
case Spades = "♠"
case Hearts = "♥"
case Diamonds = "♦"
case Clubs = "♣"
}
for f in iterateEnum(Suit) {
println(f.rawValue)
}
salidas:
♠
♥
♦
♣
Pero, esto es solo para fines de prueba o depuración : se basa en varios comportamientos de compilación de corriente no documentada (Swift1.1). A fin de utilizarlo en 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 de memoria de
enum
(excluyendoenum
s con tipos asociados) es solo un índice de casos, cuando el recuento de los casos es2...256
, es idéntico aUInt8
, cuando257...65536
, esUInt16
y así sucesivamente. Por lo tanto, puede serunsafeBitcast
de los tipos de enteros sin signo correspondientes. -
.hashValue
de valores de enumeración es el mismo que el índice del caso. -
.hashValue
de valores de enumeración bitcasted de índice no válido es0
ADICIONAL:
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
}
}
AÑADIDO: 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
}
}
AÑADIDO: 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
}
}
Las otras soluciones funcionan, pero todas hacen suposiciones de, por ejemplo, el número de posibles rangos y demandas, o cuál puede ser el primero y el último. Es cierto que el diseño de un mazo de cartas probablemente no cambiará mucho en el futuro previsible. En general, sin embargo, es mejor escribir código que haga las más bajas suposiciones posibles. Mi solución:
He agregado un tipo sin procesar a la enumeración del traje, por lo que puedo usar Suit (rawValue :) para acceder a los casos 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)
}
}
}
Debajo de la implementación del método createDeck () de Card. init (rawValue :) es un inicializador failable y devuelve un opcional. Al desempaquetar y verificar su valor en ambas afirmaciones, no es necesario asumir el número de casos de Rango o Demanda:
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 método createDeck:
let card = Card(rank: Rank.Ace, suit: Suit.Clubs)
let deck = card.createDeck()
Lo siento, mi respuesta fue específica a cómo usé esta publicación en lo que necesitaba hacer. Para aquellos que se encuentran con esta pregunta, buscando una manera de encontrar un caso dentro de una enumeración, esta es la forma de hacerlo (nuevo en Swift 2):
Edición: camelCase en minúsculas ahora es el estándar para los valores 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 acerca de la enumeración en una enumeración, las respuestas dadas en esta página que incluyen una var / let estática que contiene una matriz de todos los valores de enumeración son correctas. El último código de ejemplo de Apple para tvOS contiene esta misma técnica.
Dicho esto, deberían incorporar un mecanismo más conveniente en el lenguaje (Apple, ¿estás escuchando?).
Me encontré haciendo todos los .allValues
largo de mi código. Finalmente, descubrí una manera de simplemente ajustarme a un protocolo de Iteratable
y tener un método 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"]
Me gusta esta solución que armé después de encontrar esta página: enumerar comprensión en Swift
Utiliza int raws en lugar de cadenas, pero evita escribir dos veces, permite personalizar los rangos y no codifica valores brutos.
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 withJoker: Bool) -> [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
}
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)
}
Podrías recorrer una enumeración implementando el protocolo ForwardIndexType
.
El protocolo ForwardIndexType
requiere que defina una función successor()
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 función successor()
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
}
Puedes tratar de 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]
Si le da a la enumeración un valor Int en bruto , hará que el bucle sea mucho más fácil.
Por ejemplo, puede usar anyGenerator
para 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, este parece ser un patrón bastante común, ¿no sería bueno si pudiéramos hacer que cualquier tipo de enumeración sea enumerable simplemente por un protocolo? Bueno, con Swift 2.0 y extensiones de protocolo, ¡ahora podemos!
Simplemente añada 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 hacerlo enumerable de acuerdo con el 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 método firstRawValue
:
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 Suit, incluida la sustitución de simpleDescription
con 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)
}
EDITAR:
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
}
}
Aquí, un método que utilizo para iterar una enumeración y proporcionar varios tipos de valores a partir de una enumeración
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: septiembre, español: siete, japonés: shichi
Total de 8 casos
siete en japonés: shichi
ACTUALIZAR
Recientemente he creado 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
}
}
Enums tiene los métodos toRaw () y fromRaw (), por lo que si su valor bruto es un Int, puede iterar desde el primero hasta el último enumeración:
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 método simpleDescription, por lo que primero establecemos convertSuit en nuestro valor y luego configuramos una constante en conversionSuit.simpleDescription ()
Esto parece un hack, pero si usas valores en bruto puedes hacer algo como esto
enum Suit: Int {
case Spades = 0, Hearts, Diamonds, Clubs
...
}
var suitIndex = 0
while var suit = Suit.fromRaw(suitIndex++) {
...
}
Hay una forma inteligente, y frustrante, ya que ilustra la diferencia entre los dos tipos diferentes de enumeraciones.
Prueba esto:
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
}
El acuerdo es que una enumeración respaldada por números (valores sin procesar) está implícitamente ordenada explícitamente, mientras que una enumeración que no está respaldada por números está explícitamente implícitamente desordenada.
Por ejemplo, cuando asignamos números a los valores de enumeración, el lenguaje es lo suficientemente astuto como para determinar en qué orden están los números. Si, por otro lado, no le damos ningún orden, cuando intentamos iterar sobre los valores que el idioma arroja. manos arriba en el aire y dice "sí, pero ¿a cuál quieres ir primero?"
Otros idiomas que pueden hacer esto (iterar sobre enumerados no ordenados) pueden ser los mismos idiomas en los que todo está ''bajo el capó'' en realidad un mapa o diccionario, y puede iterar sobre las teclas de un mapa, ya sea que haya algún orden lógico o no.
Entonces, el truco es proporcionarle algo que esté explícitamente ordenado, en este caso instancias de los trajes en una matriz en el orden que deseamos. Tan pronto como le das eso, Swift dice "bueno, ¿por qué no lo dijiste en primer lugar?"
El otro truco de la taquigrafía es usar el operador forzado en la función fromRaw. Esto ilustra otro ''gotcha'' sobre las enumeraciones, que el rango de valores posibles para pasar es a menudo más grande que el rango de las enumeraciones. Por ejemplo, si dijéramos Rank.fromRaw (60) no se devolvería un valor, por lo que estamos usando la función opcional del idioma y, cuando comencemos a usar opciones, pronto habrá un forzado. (O, alternativamente, la construcción if, que aún me parece un poco rara)
He utilizado el siguiente método, asumiendo que sé cuál es el último valor en la enumeración de Rango y todos los rangos tienen valores incrementales después de Ace
Prefiero esta manera ya que es limpia y pequeña, fácil de entender
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
}
Al igual que con @Kametrixom respuesta here creo que devuelve una matriz sería mejor que volver AnySequence, ya que se puede tener acceso a todas las chucherías de matriz como el recuento, etc.
Aquí está la re-escritura:
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)
}
}
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 como propiedades primera y última. Agrega solo dos líneas de código a cada enumeración: aún un poco rígido, pero al menos no duplica todo el conjunto. Requiere la modificación de la enumeración del Traje para que sea un Int, como lo es la enumeración de Rango, en lugar de sin tipo.
En lugar de repetir toda la solución, aquí está el código que agregué a la enumeración de Rango, en algún lugar después de las declaraciones del caso (la enumeración del traje 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 establecía cómo se estructuraría 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
}
Es insatisfactorio 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 que dijera Rank.first en lugar de Rank.Ace.first. Funciona (con cualquier elemento), pero es feo. ¿Puede alguien mostrar cómo elevar eso al nivel de enumeración?
Y para hacerlo funcionar, saqué el método createDeck de la estructura de la tarjeta ... no pude averiguar cómo obtener una matriz [String] devuelta desde esa estructura, y ese parece un mal lugar para poner ese método de todos modos.
Este es un post bastante antiguo, de Swift 2.0. Ahora hay algunas soluciones mejores aquí que utilizan las características más nuevas de swift 3.0: Iterar a través de un Enum en Swift 3.0
Y en esta pregunta hay una solución que usa una nueva característica de (la versión no publicada aún mientras escribo esta edición) Swift 4.2: ¿Cómo obtengo el recuento de una enumeración Swift?
Hay muchas buenas soluciones en este hilo y otras, sin embargo, algunas 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
}
}
Iterar
for item in Number.allValues {
print("number is: /(item)")
}
Lo hice utilizando una propiedad computada, 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 utiliza valores sin procesar int, pero no necesito repetir todos los miembros de la enumeración en una propiedad separada.
ACTUALIZACIÓN Xcode 6.1 cambió un poco la manera de obtener miembros de la enumeración usando un valor en bruto, por lo que arreglé el listado. También se corrigió pequeño error con el primer valor bruto incorrecto
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++)
}
}
)
}
}
Mientras que tratar Swift 2.0
aquí es mi sugerencia:
He añadido el tipo raw 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
}
}
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?