Wie zähle ich eine Aufzählung mit dem String-Typ auf?

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

Wie kann ich zum Beispiel Folgendes tun:

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

Resultierendes Beispiel:

♠
♥
♦
♣
Lucien
quelle
In welchem ​​Fall würden Sie den Typ nicht kennen?
Mtbennett
Sie haben Recht, in diesem Fall ist es ein String-Typ.
Lucien
Noch keine Reflexion in Swift ...
Sulthan
22
Ist es nicht ironisch, dass sie Aufzählungen genannt werden, aber es ist so schmerzhaft ärgerlich, in Swift aufzuzählen?
Charlton Provatas
3
@CharltonProvatas Wenn das der einzige Nachteil in Swift wäre, würde ich es einen Tag nennen. Wenn ich mir anschaue, wie viele Leute unterschiedliche Problemumgehungen dafür anbieten, nage ich nur an meiner Tastatur.
QWERTY_SO

Antworten:

267

Swift 4.2+

Beginnen Sie mit Swift 4.2 (mit Xcode 10) und fügen Sie einfach die Protokollkonformität hinzu, um davon CaseIterablezu profitieren allCases. Um diese Protokollkonformität hinzuzufügen, müssen Sie einfach irgendwo schreiben:

extension Suit: CaseIterable {}

Wenn die Aufzählung Ihre eigene ist, können Sie die Konformität direkt in der Erklärung angeben:

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

Dann druckt der folgende Code alle möglichen Werte:

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

Kompatibilität mit früheren Swift-Versionen (3.x und 4.x)

Wenn Sie Swift 3.x oder 4.0 unterstützen müssen, können Sie die Swift 4.2-Implementierung nachahmen, indem Sie den folgenden Code hinzufügen:

#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
Cœur
quelle
@DmitryPetukhov Ich würde gerne helfen, aber: (1) Sind Sie sicher, dass Sie die neueste Version des Codes haben? (ein Absturz wurde vor einem Monat behoben) und (2) Bitte geben Sie eine MCVE Ihres benutzerdefinierten Typs, der einen Absturz reproduzieren kann, und Ihre Version von Xcode an.
Cœur
Dies funktioniert gut für mich bei Debug-Builds, aber sobald ich eine Version erstelle und auf TestFlight hochlade, stürzt sie ab. Zieht Apple das irgendwie aus?
Daniel Wood
1
Es scheint, dass Ihre Version eine positive gegenüber der eingebauten Version von CaseIterator hat. Ich kann Aufzählungen, die in einer anderen Datei definiert sind, mit Ihrer Version erweitern. Wenn Sie allCases in der Erweiterung öffentlich machen, können Sie auch in verschiedenen Frameworks definierte Aufzählungen erweitern.
Adamfowlerphoto
1
@CyberMew Ich habe die Antwort aktualisiert, um sie zu verdeutlichen. Das Swift-Buch und die Xcode 10-Versionshinweise zu CaseIterable stehen hinter meiner Antwort und sie verwendeten vereinfachte Beispiele, bei denen die Aufzählung Stringim Gegensatz zur vorliegenden Frage zum Stapelüberlauf nicht unterstützt wird .
Cœur
1
Ich möchte die Wichtigkeit des "# if! Swift (> = 4.2)" hervorheben. Wenn Sie Ihren Code vor Swift 4.2 geschrieben haben und vergessen haben, den folgenden Code zu entfernen: "# if! Swift (> = 4.2)", wenn Sie Xcode Version 11.4 verwenden, um lokal auf Ihrem Testgerät zu erstellen, ist alles in Ordnung. Wenn Ihre App jedoch aus dem App Store oder dem Testflug heruntergeladen wird, stürzt dieser Code Ihre App ab. Diese Art von Fehler ist sehr schwer zu erkennen oder zu debuggen.
Oliver Zhang
524

Dieser Beitrag ist hier relevant https://www.swift-studies.com/blog/2014/6/10/enumerating-enums-in-swift

Im Wesentlichen ist die vorgeschlagene Lösung

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

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

for category in ProductCategory.allValues{
     //Do something
}
rougeExciter
quelle
188
Schön, aber ... Sie müssen Ihre Elemente der Aufzählung zweimal eingeben - einmal für die Aufzählung, einmal für die allValues. Nicht gerade so elegant, wie man es sich wünscht.
Jay Imerman
2
Stimmen Sie dem "aber" zu ... aber wie im Artikel angegeben, gibt es vielleicht ein Problem, dass eine Aufzählung wirklich eine Menge und daher ungeordnet ist ... wohlgemerkt ... Bestellfälle, die in definiert sind, wären kein schlechter Start!
RougeExciter
3
In Java erledigt der Compiler dies für Sie, möglicherweise erledigt Swift 2.0 dies auch. Insbesondere in Java erhalten alle Enums eine Beschreibungsmethode (toString in Java), die den String als Fallnamen (Washers, ...) angibt, und eine Reihe von Fällen wird automatisch erstellt. Java bietet Ihnen auch eine Positionsindizierung. Wie gesagt, vielleicht Swift 2.0.
Howard Lovatt
1
Idealerweise haben Sie etwas Ähnliches wie die c # -Implementierung, das Sie ausführen können, das Enum.Values(typeof(FooEnum))jedoch als Erweiterungsmethode verfügbar gemacht wird (z. B. Map oder Reduce). FooEnum.values() :: values(EnumType -> [EnumType])
Rodrigoelp
1
Der Artikel macht gleich am Ende einen guten Punkt darüber, dass jeder Aufzählungswert mit einem Komponententest im Array allValues ​​enthalten ist. Ich kann jedoch immer noch jemanden sehen, der weitere Elemente hinzufügt, diese jedoch im Komponententest nicht erzwingt, wodurch wir am Anfang immer noch zurückbleiben und nicht sicher sind, dass alle Aufzählungswerte in allValues ​​gespeichert sind.
DonnaLea
278

Ich habe eine Utility-Funktion iterateEnum()zum Iterieren von Fällen für beliebige enumTypen erstellt.

Hier ist die Beispielverwendung:

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

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

Welche Ausgänge:

♠
♥
♦
♣

Dies dient jedoch nur zu Debug- oder Testzwecken: Dies hängt von mehreren undokumentierten Verhaltensweisen des Swift1.1-Compilers ab. Verwenden Sie ihn daher auf eigenes Risiko.

Hier ist der 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
    }
}

Die zugrunde liegende Idee ist:

  • Die Speicherdarstellung von enum, mit Ausnahme von enums mit zugehörigen Typen, ist nur ein Index von Fällen, wenn die Anzahl der Fälle 2...256identisch ist mit UInt8, wann 257...65536, UInt16und so weiter. Es kann sich also um unsafeBitcastentsprechende vorzeichenlose Ganzzahltypen handeln.
  • .hashValue Die Anzahl der Enum-Werte entspricht dem Index des Falls.
  • .hashValuevon Enum-Werten, die vom ungültigen Index bitcasted sind, ist 0.

Überarbeitet für Swift2 und implementierte Casting-Ideen aus der Antwort von @ 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
    }
}

Überarbeitet für 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
    }
}

Überarbeitet für 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
quelle
20
Genial und die einzige Antwort, die die Frage beantwortet! Aber ja ... werde es nicht anfassen! +1 für Mühe!
dotToString
Ich habe gerade meine Antwort gepostet , die im Grunde genauso funktioniert (habe diese Antwort erst später gesehen). Es verwendet Swift 2.0 Beta 6 und die modernen Funktionen der Sprache.
Kametrixom
2
Swift 3 Version funktioniert gut. Nur um die Verwendung ein wenig zu ändern: für f in iterateEnum (Suit.self) {print (f.rawValue)}
Gene De Lisa
16
+1 das ist ziemlich genial. IMHO ist es auch zu clever, um es zu verwenden, was sich daran zeigt, dass es bei jeder größeren Änderung der Swift-Version erheblich bricht. Zur Ehre des Autors wurde die Swift 3-Version einen Monat vor dem Ende der Beta-Version von Swift 3 erstellt ... Wenn Sie diese Antwort nehmen und all dies withUnsafePointer withMemoryReboundund pointeeandere Dinge lernen wollen , dann verwenden Sie diese auf jeden Fall. Sonst würde ich es vermeiden.
Dan Rosenstark
5
Ich möchte nur hinzufügen, dass dies jetzt in Swift 4 kaputt ist, aber nur unter Linux, also +1 zu den obigen Kommentaren, dass dies zu klug ist, um es zu verwenden.
Andrew Plummer
130

Die anderen Lösungen funktionieren, aber alle gehen beispielsweise von der Anzahl der möglichen Ränge und Anzüge oder dem ersten und letzten Rang aus. Das Layout eines Kartenspiels wird sich in absehbarer Zeit wahrscheinlich nicht wesentlich ändern. Im Allgemeinen ist es jedoch ordentlicher, Code zu schreiben, der so wenig Annahmen wie möglich macht. Meine Lösung:

Ich habe der SuitAufzählung einen Rohtyp hinzugefügt , damit ich Suit(rawValue:)auf die SuitFälle zugreifen kann :

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

Nachfolgend die Implementierung der Card- createDeck()Methode. init(rawValue:)ist ein fehlgeschlagener Initialisierer und gibt einen optionalen zurück. Wenn Sie den Wert in beiden while-Anweisungen auspacken und überprüfen, müssen Sie nicht die Anzahl Rankoder die SuitFälle annehmen :

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

Hier erfahren Sie, wie Sie das aufrufen createDeck Methode auf:

let card = Card(rank: Rank.Ace, suit: Suit.Clubs)
let deck = card.createDeck()
sdduursma
quelle
12
Absolut BESTE Antwort, die ich in verschiedenen Threads zu diesem Thema gesehen habe. Sehr elegant. Dies funktioniert mit Aufzählungen vom Typ Int, aber ich frage mich, wie man andere Typen (Zeichenfolgen, benutzerdefinierte Typen usw.) durchlaufen kann.
Jay Imerman
3
Dies ist definitiv die beste Lösung. Eine Sache zu beachten. Im Beispiel im Buch gibt es nicht "case Spades = 1" wie bei sdduursma. Ich habe das zuerst nicht verstanden. das ist eine Option, oder Sie können einfach "var m = 0"
Joshua Goossen
10
Dies setzt voraus, dass die Rohwerte sequentiell sind. Wenn dies nicht der Fall ist, z. B. wenn die Aufzählung Bitmaskenflags darstellt, wird die Schleife vorzeitig beendet.
Jim
1
Diese Lösung setzt voraus, dass Sie die Definition von ändern können Suit. Sie können dies in diesem Beispiel tun, aber die Übung sollte Sie dazu bringen, mit dem zu arbeiten, das enumsIhnen gegeben wurde, als ob sie von einer externen Quelle stammten.
Josh J
1
Das einzige, worüber ich mich beschweren möchte, ist, dass ich es nicht als statische Methode aufrufen kann, sondern zuerst ein Kartenobjekt erstellen muss.
Qed
76

Ich stolperte in den Bits und Bytes herum und erstellte eine Erweiterung, von der ich später herausfand, dass sie der Antwort von @rintaro sehr ähnlich ist . Es wird so verwendet:

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

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

Bemerkenswert ist, dass es für jede Aufzählung ohne zugehörige Werte verwendet werden kann. Beachten Sie, dass dies für Aufzählungen ohne Fälle nicht funktioniert.

Wie bei der Antwort von @rintaro verwendet dieser Code die zugrunde liegende Darstellung einer Aufzählung. Diese Darstellung ist nicht dokumentiert und könnte sich in Zukunft ändern, was sie beschädigen würde. Ich empfehle die Verwendung in der Produktion nicht.

Code (Swift 2.2, Xcode 7.3.1, funktioniert nicht mit 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, funktioniert nicht mit 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
            }
        }
    }
}

Ich habe keine Ahnung, warum ich brauche typealias, aber der Compiler beschwert sich ohne es.

Kametrixom
quelle
10
Diese Antwort ist sogar besser als meine Antwort, besonders im Casting-Teil :)
Rintaro
Aber ich denke, das funktioniert nur in Little Endian-Umgebungen?
Rintaro
5
Xcode 8 Beta 6 hat dies wieder geändert! Ich erhalte die folgende Fehlermeldung: "init" ist nicht verfügbar: Verwenden Sie "withMemoryRebound (to: Capacity: _)", um den Speicher vorübergehend als einen anderen layoutkompatiblen Typ anzuzeigen. "
Confused
1
@ConfusedVorlon: siehe Antwort oben von @Rintaro: ersetzen withUnsafePointer... pointee}durchwithUnsafePointer(to: &i) { $0.withMemoryRebound(to: T.self, capacity: 1) { $0.pointee } }
Stefan
1
Ab
26

Sie können eine Aufzählung durchlaufen, indem Sie das ForwardIndexTypeProtokoll implementieren .

Für das ForwardIndexTypeProtokoll müssen Sie eine successor()Funktion definieren, um die Elemente zu durchlaufen.

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

Wenn Sie über einen offenen oder geschlossenen Bereich ( ..<oder ...) iterieren, wird intern die successor()Funktion aufgerufen, mit der Sie Folgendes schreiben können:

// 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
quelle
2
Ich finde, dass dies die "richtigste" Antwort auf die Frage ist, selbst die "eleganteste" (zusätzlicher Code, der gegenüber anderen Optionen hier erforderlich ist) angesichts der resultierenden Syntax, wenn sie innerhalb eines Bereichs verwendet wird (die Syntax ist das, was ich bin würde erwarten, in der Lage zu sein, wenn Aufzählungen ohne Problemumgehungen aufzählbar sind). Vielen Dank! Obwohl es erwähnenswert ist, dass das erzwungene Auspacken offensichtlich gefährlich ist, wenn die Überladung des Bedieners an anderer Stelle als Nachfolger () eingesetzt wird (was verlockend erscheint). Auch das Infix scheint unnötig ...?
iOS Gamer
Aktualisierte Antwort, um die neuesten Swift-Sprachspezifikationen
widerzuspiegeln
Eine richtig definierte successor()Methode (erste Option) würde die Notwendigkeit enumeines zugeordneten Typs beseitigen . +1
nhgrif
1
Aber diese elegante Antwort funktioniert nicht für String-Enums, oder?
Ali
Die "richtige" / Best-Practice-Lösung! + 1-ed
Siu Ching Pong -Asuka Kenji-
18

Dieses Problem ist jetzt viel einfacher. Hier ist meine Swift 4.2-Lösung:

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:

Ich mag diese Lösung, die ich zusammengestellt habe, nachdem ich " Listenverständnis in Swift " gefunden habe.

Es verwendet Int-Rohdaten anstelle von Zeichenfolgen, vermeidet jedoch das zweimalige Eingeben, ermöglicht das Anpassen der Bereiche und codiert Rohwerte nicht fest.

Dies ist eine Swift 4-Version meiner ursprünglichen Lösung, aber siehe die 4.2-Verbesserung oben:

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
quelle
Oh, ich sehe jetzt, dass meine im Grunde die gleiche ist wie die von Sutean Rutjanalard.
Adazacom
1
Eigentlich hat mir Ihre Implementierung besser gefallen. Ich denke es ist klarer! 1 Gegenstimme. Tatsächlich sind die am häufigsten gewählten Antworten zu klug und werden mit Sicherheit in Zukunft brechen. Ihre verspricht etwas Stabilität in der Zukunft.
Jvarela
17

Im Prinzip ist dies auf diese Weise möglich, vorausgesetzt, Sie verwenden keine Rohwertzuweisung für die Fälle von 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
quelle
7
Das ist schön, aber es funktioniert nur für fortlaufende Integer-Enums ab 0
Robert
@ Robert, wie mein Kommentar oben sagt: "Sie verwenden keine
Rohwertzuweisung
Ja - verwenden Sie keine Rohwerte und setzen Sie den zugrunde liegenden Typ nicht auf int. In Swift benötigen Sie keinen Typ für eine Aufzählung wie im Beispiel für Anzüge. enum ItWontWorkForThisEnum {case a, b, c}
Robert
Wie würde dies das Problem beheben, wenn dem Aufzählungsfall ein Tupel zugeordnet ist?
RougeExciter
Sie können ein Tupel nicht einfach mit einer Aufzählung verknüpfen.
nhgrif
13

Wenn Sie der Aufzählung einen rohen Int-Wert geben , wird das Schleifen viel einfacher.

Sie können beispielsweise anyGeneratoreinen Generator verwenden, der Ihre Werte auflistet:

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

Dies sieht jedoch nach einem ziemlich häufigen Muster aus. Wäre es nicht schön, wenn wir einen Aufzählungstyp durch einfache Anpassung an ein Protokoll aufzählbar machen könnten? Nun, mit Swift 2.0 und Protokollerweiterungen können wir jetzt!

Fügen Sie dies einfach Ihrem Projekt hinzu:

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

Jedes Mal, wenn Sie eine Aufzählung erstellen (solange sie einen Int-Rohwert hat), können Sie sie durch Konformität mit dem Protokoll aufzählbar machen:

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

Wenn Ihre Aufzählungswerte nicht mit 0(Standardeinstellung) beginnen, überschreiben Sie die firstRawValueMethode:

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

Die letzte Suit-Klasse, einschließlich des Ersetzens simpleDescriptiondurch das Standardprotokoll CustomStringConvertible , sieht folgendermaßen aus:

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

Swift 3-Syntax:

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
    }
}
Sinnvoll
quelle
nextIndex ++ wird in Kürze 3 entfernt. Was schlagen Sie als Ersatz für - var nextIndex = firstRawValue () vor? anyGenerator {Self (rawValue: nextIndex ++)}
Avi
Herausgefunden. defer {nextIndex + = 1} return AnyGenerator {Self (rawValue: nextIndex)}
Avi
12

Auf Swift 2.2 + aktualisiert

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

Der Code wurde auf das Swift 2.2-Formular aktualisiert @ Kametrixoms Antwort

Für Swift 3.0+ (vielen Dank an @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
quelle
@silvansky könntest du bitte erklären was du meinst?
Ale_stro
Ups, sorry, ich habe es noch einmal überprüft und es gab einen Spielplatzfehler: In einem echten Projekt funktioniert dieser Code wie erwartet, danke! =)
Silvansky
1
Tolle Lösung! Funktioniert auch gut mit Swift 3 mit minimalen Änderungen ('AnyGenerator' umbenannt in 'AnyIterator' und '.memory' umbenannt in '.pointee').
Philip
9

Swift 5 Lösung:

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

Ich habe .allValuesin meinem gesamten Code viel getan . Ich habe endlich einen Weg gefunden, einfach einem IteratableProtokoll zu entsprechen und eine rawValues()Methode zu haben.

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

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

Nennen

print(Filter.allValues)

Drucke:

["Gehalt", "Erfahrung", "Technologie", "Nicht genutzt", "Nicht genutzter hoher Wert"]


ältere Versionen

Zur enumDarstellungInt

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

Nennen Sie es so:

print(Filter.allValues)

Drucke:

[0, 1, 2, 3, 4]


Zur enumDarstellungString

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

Nennen

print(Filter.allValues)

Drucke:

["Gehalt", "Erfahrung", "Technologie", "Nicht genutzt", "Nicht genutzter hoher Wert"]

Warif Akhand Rishi
quelle
7

BEARBEITEN: Swift Evolution-Vorschlag SE-0194 Die abgeleitete Sammlung von Enum-Fällen schlägt eine Lösung für dieses Problem vor. Wir sehen es in Swift 4.2 und neuer. Der Vorschlag weist auch auf einige Problemumgehungen hin , die den hier bereits erwähnten ähnlich sind, aber es könnte dennoch interessant sein, sie zu sehen.

Der Vollständigkeit halber werde ich auch meinen ursprünglichen Beitrag behalten.


Dies ist ein weiterer Ansatz, der auf der Antwort von @ Peymmankh basiert und an Swift 3 angepasst ist .

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

Wie wäre es damit?

Mossila
quelle
Danke, es funktioniert so wie ich es brauche. Aber gibt es beim Schließen der Karte eine Möglichkeit, den Wert nicht nach Index, sondern nach Namen zu ermitteln?
Mikhail
4

Sie können versuchen, so aufzuzählen

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
quelle
1
Das ist viel nutzloser Code! Es ist gleichbedeutend mit static var enumerate = [Mercury, Venus, Earth, Mars]einer unterdurchschnittlichen Antwort im Vergleich zu den am häufigsten gewählten
Cœur
@ Cœur diese Antwort hat den wichtigen Vorteil, den Compiler zu verwenden, um sicherzustellen, dass Sie keinen Fall verpassen.
Dchakarov
@ Cœur, die das gleiche Problem hat, dass Sie einen Benutzerfehler machen können, dh der Compiler wird sich nicht beschweren, wenn Sie return [Mercury, Venus, Mars]anstelle vonreturn [Mercury, Venus, Earth, Mars]
dchakarov
@dchakarov Ich habe mich aus Gründen der Klarheit entschlossen, die Verbesserung als Antwort zu veröffentlichen: stackoverflow.com/a/50409525/1033581
Cœur
@ Cœur Wenn Sie in Ihrer neuen Antwort die return-Anweisung durch diese ersetzen, return [.spades, .hearts, .clubs]sagt der Compiler nichts und wenn Sie versuchen, sie im Code zu verwenden, erhalten Sie [TestApp.Suit.spades, TestApp.Suit.hearts, TestApp.Suit.clubs]- das war mein Punkt - das, wenn Sie es mit einem großen zu tun haben enum und Sie müssen von Zeit zu Zeit Fälle hinzufügen oder entfernen. Ihre Lösung ist anfällig für Auslassungsfehler, während die aktuelle Antwort zwar nicht präzise, ​​aber sicherer ist.
Dchakarov
4

In Swift 3 können rawValueSie das StrideableProtokoll implementieren , wenn die zugrunde liegende Aufzählung vorhanden ist . Die Vorteile sind, dass keine Arrays von Werten wie in einigen anderen Vorschlägen erstellt werden und dass die Standard-Swift-Schleife "for in" funktioniert, was eine schöne Syntax ergibt.

// "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
quelle
Ahh, genau das, wonach ich gesucht habe, um ForwardIndexType zu ersetzen. Jetzt sehen meine Iterationen auf der Verwendungsseite gut aus ... genau auf die richtige Art und Weise.
Andrew Duncan
4

Diese Lösung bietet das richtige Gleichgewicht zwischen Lesbarkeit und Wartbarkeit.

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()
Andrew
quelle
Meiner Meinung nach ist dies die beste Lösung. Wenn ich schnellen Code sehe, ist die Lesbarkeit meistens nicht besser als objc. Aber es könnte sein, wenn Programmierer den Lesern ihres Codes mehr Aufmerksamkeit schenken . Zum Beispiel ihr zukünftiges Selbst :)
Vilém Kurz
4

Entschuldigung, meine Antwort war spezifisch dafür, wie ich diesen Beitrag in dem verwendet habe, was ich tun musste. Für diejenigen, die über diese Frage stolpern und nach einem Weg suchen, einen Fall innerhalb einer Aufzählung zu finden, ist dies der Weg, dies zu tun (neu in Swift 2):

Bearbeiten: Kleinbuchstaben camelCase ist jetzt der Standard für Swift 3-Aufzählungswerte

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

Für diejenigen, die sich über die Aufzählung in einer Aufzählung wundern, sind die Antworten auf dieser Seite, die eine statische Variable / let enthalten, die ein Array aller Aufzählungswerte enthält, korrekt. Der neueste Apple-Beispielcode für tvOS enthält genau dieselbe Technik.

Davon abgesehen sollten sie einen bequemeren Mechanismus in die Sprache einbauen (Apple, hören Sie zu?)!

Gene Loparco
quelle
3

Das Experiment war: EXPERIMENT

Fügen Sie der Karte eine Methode hinzu, mit der ein vollständiges Kartenspiel mit einer Karte aus jeder Kombination aus Rang und Farbe erstellt wird.

Ohne den angegebenen Code zu ändern oder zu verbessern, außer die Methode hinzuzufügen (und ohne Dinge zu verwenden, die noch nicht gelehrt wurden), habe ich diese Lösung gefunden:

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
quelle
3

Hier ist eine Methode, mit der ich einen enumWertetyp von einem iteriere und mehrere bereitstelleenum

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

Dies ist die Ausgabe:

Die Nummer Null auf Französisch: Zéro, Spanisch: Cero, Japanisch: Nuru
Die Nummer Eins auf Französisch: Un, Spanisch: Uno, Japanisch: Ichi
Die Nummer Zwei auf Französisch: Deux, Spanisch: Dos, Japanisch: Ni
Die Nummer Drei auf Französisch : trois, spanisch: tres, japanisch: san
Die Nummer Vier auf Französisch: Quatre, Spanisch: Cuatro, Japanisch: Shi
Die Nummer Fünf auf Französisch: Cinq, Spanisch: Cinco, Japanisch: Go
Die Nummer Sechs auf Französisch: Sechs, Spanisch: seis, japanisch: roku
Die Nummer Sieben auf Französisch: sept, spanisch: siete, japanisch: shichi

Insgesamt 8 Fälle

siete auf japanisch: shichi


AKTUALISIEREN

Ich habe kürzlich ein Protokoll für die Aufzählung erstellt. Das Protokoll erfordert eine Aufzählung mit einem Int-Rohwert:

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
quelle
2

Dies scheint ein Hack zu sein, aber wenn Sie Rohwerte verwenden, können Sie so etwas tun

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

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

Während Swift 2.0ich mich hier beschäftige, ist mein Vorschlag:

Ich habe den Rohtyp hinzugefügt Suit enum

enum Suit: Int {

dann:

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
quelle
2

Wie bei @Kametrixom hier antworten glaube ich, dass die Rückgabe eines Arrays besser wäre als die Rückgabe von AnySequence, da Sie auf alle Extras des Arrays wie count usw. zugreifen können.

Hier ist das Umschreiben:

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
quelle
2

Eine andere Lösung:

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
quelle
2

Dies ist ein ziemlich alter Beitrag von Swift 2.0. Hier gibt es jetzt einige bessere Lösungen, die neuere Funktionen von Swift 3.0 verwenden: Durchlaufen einer Aufzählung in Swift 3.0

Und zu dieser Frage gibt es eine Lösung, die eine neue Funktion von Swift 4.2 verwendet (die noch nicht veröffentlicht wurde, während ich diese Bearbeitung schreibe): Wie erhalte ich die Anzahl einer Swift-Enumeration?


Es gibt viele gute Lösungen in diesem Thread und andere, einige davon sind jedoch sehr kompliziert. Ich möchte so viel wie möglich vereinfachen. Hier ist eine Lösung, die möglicherweise für unterschiedliche Anforderungen funktioniert oder nicht, aber ich denke, dass sie in den meisten Fällen gut funktioniert:

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

So wiederholen Sie:

for item in Number.allValues {
    print("number is: \(item)")
}
Abbey Jackson
quelle
1
Das fühlt sich nach viel Arbeit an, die spezifisch für die von Ihnen erstellte individuelle Aufzählung ist. Ich bin mir nicht sicher, ob die Rückgabe von [Number.One.rawValue, Number.Two.rawValue, ...] in diesem Fall nicht sauberer ist .
Scott Austin
Dies ist ein ziemlich alter Beitrag von Swift 2.0. Es gibt jetzt einige bessere Lösungen, die neuere Funktionen von Swift 3.0 verwenden: stackoverflow.com/questions/41352594/… Und zu dieser Frage gibt es eine Lösung, die eine neue Funktion von (die noch nicht veröffentlichte, während ich diese Bearbeitung schreibe) verwendet ) Swift 4.2: stackoverflow.com/questions/27094878/…
Abbey Jackson
2

Aufzählungen haben toRaw()und fromRaw()Methoden. Wenn Ihr Rohwert also ein ist Int, können Sie vom ersten bis zum letzten iterieren 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()
    }
}

Ein Problem ist, dass Sie vor dem Ausführen der simpleDescriptionMethode auf optionale Werte testen müssen. Daher setzen convertedSuitwir zuerst unseren Wert und dann eine Konstante aufconvertedSuit.simpleDescription()

Adrian Harris Crowne
quelle
2
Die ursprüngliche Frage
betraf
2

Hier ist mein vorgeschlagener Ansatz. Es ist nicht ganz zufriedenstellend (ich bin sehr neu bei Swift und OOP!), Aber vielleicht kann es jemand verfeinern. Die Idee ist, dass jede Aufzählung ihre eigenen Bereichsinformationen .firstund .lastEigenschaften bereitstellt . Es werden nur zwei Codezeilen zu jeder Aufzählung hinzugefügt: immer noch ein bisschen hartcodiert, aber zumindest wird nicht der gesamte Satz dupliziert. Es ist erforderlich, die SuitAufzählung so zu ändern, dass sie ein Int wie das istRank Aufzählung ist, anstatt untypisiert zu sein.

Anstatt die gesamte Lösung .wiederzugeben, ist hier der Code, den ich irgendwo nach den case-Anweisungen zur Aufzählung hinzugefügt habe ( SuitAufzählung ist ähnlich):

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

und die Schleife, mit der ich das Deck als Array von String erstellt habe. (Die Problemdefinition gab nicht an, wie das Deck strukturiert werden sollte.)

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
}

Dies ist unbefriedigend, da die Eigenschaften eher einem Element als der Aufzählung zugeordnet sind. Aber es fügt den 'for'-Schleifen Klarheit hinzu. Ich möchte, dass es Rank.firststatt sagtRank.Ace.first . Es funktioniert (mit jedem Element), aber es ist hässlich. Kann jemand zeigen, wie man das auf die Enum-Ebene hebt?

Und damit es funktioniert, habe ich die createDeckMethode aus der Kartenstruktur entfernt. Ich konnte nicht herausfinden, wie ein [String] -Array von dieser Struktur zurückgegeben werden kann, und das scheint ohnehin ein schlechter Ort zu sein, um eine solche Methode einzusetzen.

Roger Worden
quelle
2

Ich habe es mit einer berechneten Eigenschaft gemacht, die das Array aller Werte zurückgibt (dank dieses Beitrags http://natecook.com/blog/2014/10/loopy-random-enum-ideas/ ). Es werden jedoch auch int-Rohwerte verwendet, aber ich muss nicht alle Mitglieder der Aufzählung in einer separaten Eigenschaft wiederholen.

UPDATE Xcode 6.1 hat die Art und Weise, wie Enum-Mitglieder verwendet werden rawValue, ein wenig geändert , daher habe ich die Auflistung korrigiert. Auch kleiner Fehler mit falschem zuerst behoben 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
quelle
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
    }
}

Basierend auf Rick Antwort: Dies ist 5 mal schneller

John Lluch-Zorrilla
quelle
Durch Hinzufügen eines CountFalls werden umfassende switchImplementierungen und CaseIterableKonformitäten beeinträchtigt.
Cœur