Jak wyliczyć enum z typem String?

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

Na przykład, Jak mogę zrobić coś takiego:

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

Wynikowy przykład:

♠
♥
♦
♣
Author: Karthik Kumar, 2014-06-03

30 answers

Swift 4.2 +

Zaczynając od Swift 4.2 (z Xcode 10), wystarczy dodać zgodność protokołu do CaseIterable, Aby skorzystać z allCases:

extension Suit: CaseIterable {}

To wyświetli wszystkie możliwe wartości:

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

Zgodność z wcześniejszymi wersjami Swift (3.x i 4.x)

Wystarczy naśladować implementację 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
 94
Author: Cœur,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-30 08:54:55

Ten post jest istotny tutaj https://www.swift-studies.com/blog/2014/6/10/enumerating-enums-in-swift

Zasadniczo proponowane rozwiązanie to

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

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

for category in ProductCategory.allValues{
     //Do something
}
 498
Author: rougeExciter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-01 16:09:23

Zrobiłem funkcję użytkową iterateEnum() do iteracji przypadków dla dowolnych typów enum.

Oto przykładowe użycie:
enum Suit:String {
    case Spades = "♠"
    case Hearts = "♥"
    case Diamonds = "♦"
    case Clubs = "♣"
}

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

Wyjścia:

♠
♥
♦
♣

Ale jest to Tylko do debugowania lub testowania cel: polega to na kilku nieudokumentowanych zachowaniach kompilatora current (Swift1.1). Więc używaj go na własne ryzyko:)

Oto kod:

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

Podstawowa idea to:

  • reprezentacja pamięci enum - z wyłączeniem enum s z powiązanymi typy-to tylko indeks przypadków, gdy liczba przypadków wynosi 2...256, jest identyczna z UInt8, gdy 257...65536, jest UInt16 itd. Tak więc, może być unsafeBitcast z odpowiednich niepodpisanych typów całkowitych.
  • .hashValue wartości enum są takie same jak indeks sprawy.
  • .hashValue of enum values bitcasted from invalid index is 0

Dodano:

Poprawione dla Swift2 i wdrożone pomysły castingu z @ Kametrixom ' s odpowiedź

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

Dodano: Poprawiono dla 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
    }
}

Dodano: Poprawiono dla Swift3. 0. 1

func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
    var i = 0
    return AnyIterator {
        let next = withUnsafeBytes(of: &i) { $0.load(as: T.self) }
        if next.hashValue != i { return nil }
        i += 1
        return next
    }
}
 272
Author: rintaro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:35

Inne rozwiązaniadziałają , ale wszystkie one zakładają na przykład liczbę możliwych rang i garniturów, lub jaka może być pierwsza i ostatnia ranga. To prawda, że układ talii kart prawdopodobnie nie zmieni się zbyt wiele w przewidywalnej przyszłości. Ogólnie rzecz biorąc, lepiej jest jednak napisać kod, który tworzy jak najmniej założeń. Moje rozwiązanie:

Dodałem surowy typ do suit enum, więc mogę użyć Suit (rawValue:), aby uzyskać dostęp do Suit przypadki:

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

Poniżej implementacja metody createDeck (). init (rawValue:) jest nieudanym inicjalizatorem i zwraca opcjonalne. Po rozpakowaniu i sprawdzeniu jej wartości w obu instrukcjach while, nie ma potrzeby zakładania liczby przypadków Rank lub Suit:

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

Oto jak wywołać metodę createDeck:

let card = Card(rank: Rank.Ace, suit: Suit.Clubs)
let deck = card.createDeck()
 126
Author: sdduursma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-11 22:54:04

Druga odpowiedź, która naprawdę działa

Natknąłem się więc na bity i bajty i stworzyłem rozszerzenie (które później okazało się bardzo podobne do odpowiedzi @rintaro ). Jest użyteczny tak:

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

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

Godne uwagi jest to, że można go używać na dowolnym enum (bez powiązanych wartości). Zauważ, że to nie działa w przypadku enum, które nie mają przypadków.

Disclaimer

Podobnie jak w przypadku odpowiedzi @Rintaro, kod ten wykorzystuje podstawową reprezentację enum. Ta reprezentacja nie jest udokumentowana i może się zmienić w przyszłości, co by ją złamało -> nie polecam stosowania tego w produkcji.

Code (Swift 2.2, Xcode 7.3.1, nie działa na 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
            }
        }
    }
}

Kod (Swift 3, Xcode 8.1, nie działa na 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
            }
        }
    }
}

(nie mam pojęcia po co mi typealias, ale kompilator narzeka bez niego)

(zrobiłem dużą modyfikację tej odpowiedzi, spójrz na edycje dla poprzednich wersji)

 71
Author: Kametrixom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-13 16:25:05

Można iterację poprzez enum implementując protokół {[2] }.

Protokół ForwardIndexType wymaga zdefiniowania funkcji successor() do przechodzenia przez elementy.

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

Iteracja w zakresie otwartym lub zamkniętym (..< lub ...) wewnętrznie wywoła funkcję successor(), która pozwala zapisać to:

// Under the covers, successor(Rank.King) and successor(Rank.Ace) are called to establish limits
for r in Rank.Ace...Rank.King {
    // Do something useful
}
 25
Author: RndmTsk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-26 20:06:09

W zasadzie można to zrobić w ten sposób zakładając, że nie używa się przypisania wartości surowych dla przypadków enum:

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

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

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

for r in RankEnum.enumerate() {
  println("\(r.toRaw())")
}
 17
Author: Alfa07,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-04 07:46:29

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

To zaktualizowany kod do Swift 2.2 form @Kametrixom ' s an swer

Dla Swift 3.0+ (wielkie podziękowania dla @ 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
        }
    }
}
 13
Author: ale_stro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:45

Jeśli podasz enum surową wartość Int to znacznie ułatwi zapętlanie.

Na przykład, możesz użyć anyGenerator, aby uzyskać generator, który może wyliczyć twoje wartości:

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

Wygląda to jednak na dość powszechny wzorzec, czyż nie byłoby miło, gdybyśmy mogli uczynić dowolny typ enum możliwymi do wyliczenia, po prostu stosując się do protokołu? Cóż z Swift 2.0 i rozszerzeniami protokołu, teraz możemy!

Po prostu dodaj to do swojego projektu:

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

Teraz każdy kiedy tworzysz enum (o ile ma wartość int raw), możesz go wyliczyć zgodnie z protokołem:

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

Jeśli twoje wartości enum nie zaczynają się od 0 (domyślnie), Nadpisz metodę firstRawValue:

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

Ostateczna Klasa garnituru, w tym zastąpienie simpleDescription bardziej standardowym protokołem CustomStringConvertible , będzie wyglądać następująco:

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

EDIT:

Swift 3 składnia:

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

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

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

        return iterator
    }

    static func firstRawValue() -> Int {
        return 0
    }
}
 13
Author: Senseful,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:48

Podoba mi się to rozwiązanie, które złożyłem po znalezieniu tej strony: spis treści w języku Swift

Używa int raws zamiast ciągów znaków, ale unika dwukrotnego wpisywania, pozwala dostosować zakresy i nie koduje surowych wartości.

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
}
 9
Author: adazacom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:48

Znalazłem się robi .allValues wiele w całym moim kodzie. W końcu wymyśliłem sposób, aby po prostu dostosować się do protokołu Iteratable i mieć metodę rawValues().

protocol Iteratable {}
extension RawRepresentable where Self: RawRepresentable {

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

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

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


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

let groceryHashes = Grocery.hashValues() // AnyIterator<Grocery>
let groceryRawValues = Grocery.rawValues() // ["kroger", "h.e.b.", "randalls"]
 7
Author: Fabian Buentello,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-01 18:48:00
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
    }
}
Co ty na to?
 5
Author: Mossila,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-26 10:00:48

Edytuj: propozycja szybkiej ewolucji SE-0194 Derived Collection of Enum Cases proponuje level headed Rozwiązanie tego problemu. Widzimy to w Swift 4.2 i nowszych. We wniosku zwrócono również uwagę na niektóre[8]}obejścia [5]}, które są podobne do niektórych, o których już wspomniano, ale mimo to może być interesujące.

Zachowam również mój oryginalny post ze względu na kompletność.


To kolejne podejście oparte na odpowiedzi @Peymmankh, przystosowany do 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)
  }
}
 5
Author: ff10,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-23 09:03:24

Przepraszam, moja odpowiedź była specyficzna dla tego, jak wykorzystałem ten post w tym, co musiałem zrobić. Dla tych, którzy natkną się na to pytanie, szukając sposobu, aby znaleźć przypadek w enum, jest to sposób, aby to zrobić (nowy w Swift 2):

Edit: małe litery camelCase jest teraz standardem dla Swift 3 wartości enum

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

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

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

Dla tych, którzy zastanawiają się nad wyliczaniem na enum, odpowiedzi podane na tej stronie zawierające statyczny var/let zawierający tablicę wszystkich wartości enum są poprawne. Najnowszy przykładowy kod Apple dla tvOS zawiera dokładnie tę samą technikę.

[1]}to powiedziawszy, powinni zbudować wygodniejszy mechanizm w języku (Apple, słuchasz?)!
 4
Author: Gene Loparco,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-29 17:42:53

W Swift 3, gdy podstawowe enum ma {rawValue} s, można zaimplementować protokół {Strideable}. Zaletą jest to, że nie są tworzone tablice wartości, jak w niektórych innych sugestiach, i że standardowy Swift " dla i w ..."statement works, co sprawia, że ładna składnia.

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

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

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

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

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

// this is how you use it:
for i in MyColorEnum.Red ... MyColorEnum.Black {
    print("ENUM: \(i)")
}
 4
Author: ACK,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-20 22:49:35

Możesz spróbować wyliczyć w ten sposób

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

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

Planet.enumerate // [Mercury, Venus, Earth, Mars]
 4
Author: Karthik Kumar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-08 08:24:13

Właśnie z tym skończyłem; myślę, że to zapewnia właściwą równowagę między czytelnością i konserwacją.

struct Card {

// ...

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

let deck = Card.deck()
 4
Author: Andrew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-05 15:28:50

Eksperyment był: Eksperyment

Dodaj metodę do karty, która tworzy pełną talię kart, z jedną kartą każdej kombinacji rangi i koloru.

Więc bez modyfikowania lub ulepszania podanego kodu poza dodaniem metody (i bez używania rzeczy, które jeszcze nie zostały nauczone), wymyśliłem takie rozwiązanie:

struct Card {
    var rank: Rank
    var suit: Suit

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

    func createDeck() -> [Card] {
        var deck: [Card] = []
        for rank in Rank.Ace.rawValue...Rank.King.rawValue {
            for suit in Suit.Spades.rawValue...Suit.Clubs.rawValue {
                let card = Card(rank: Rank(rawValue: rank)!, suit: Suit(rawValue: suit)!)
                //println(card.simpleDescription())
                deck += [card]
            }
        }
        return deck
    }
}
let threeOfSpades = Card(rank: .Three, suit: .Spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()
let deck = threeOfSpades.createDeck()
 3
Author: Hans-Peter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-29 22:38:30

Tak jak w odpowiedzi @ Kametrixom Tutaj uważam, że zwracanie tablicy byłoby lepsze niż zwracanie równoważności, ponieważ możesz mieć dostęp do wszystkich dobrodziejstw tablicy, takich jak count, itp.

Oto przepis:

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)
    }
}
 3
Author: Peymankh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:32

Enums mają metody toRaw () i fromRaw (), więc jeśli twoją wartością surową jest Int, możesz iterować od pierwszego do ostatniego 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()
    }
}

Jedną z rzeczy jest to, że przed uruchomieniem metody simpleDescription trzeba przetestować wartości opcjonalne, więc najpierw ustawiamy convertedSuit na naszą wartość, a następnie ustawiamy stałą na convertedSuit.simpleDescription ()

 2
Author: Adrian Harris Crowne,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-03 09:01:38

To wygląda jak hack, ale jeśli używasz wartości raw, możesz zrobić coś takiego

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

var suitIndex = 0  
while var suit = Suit.fromRaw(suitIndex++) {  
   ...  
}  
 2
Author: KenH,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-04 01:03:26

Oto moje sugerowane podejście. Nie jest to całkowicie zadowalające (jestem bardzo nowy w Swift i OOP!) ale może ktoś to dopracuje. Chodzi o to, aby każdy enum dostarczał własne informacje o zakresie jako .pierwszy i .Ostatnie nieruchomości. Dodaje tylko dwie linijki kodu do każdego enum: nadal trochę mocno zakodowany, ale przynajmniej nie duplikuje całego zbioru. Wymaga modyfikowania Enum kombinezonu, aby było Int, jak to jest w Rank enum, zamiast untyped.

Zamiast echo całego rozwiązania, oto kod, który dodałem do Rank enum, gdzieś po poleceniach case (Suit enum jest podobny):

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

I pętla, której użyłem do zbudowania talii jako tablicy sznurków. (Definicja problemu nie określała, w jaki sposób ma być skonstruowany pokład.)

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
}

Jest to niezadowalające, ponieważ właściwości są związane z elementem, a nie z enum. Ale to dodaje jasności pętli 'for'. Chciałbym, żeby było napisane "Ranga".pierwszy zamiast rangi.Ace.najpierw. Działa (z każdym element), ale jest brzydki. Czy ktoś może pokazać jak podnieść to do poziomu enum?

I aby to działało, usunąłem metodę createDeck ze struktury karty... nie można dowiedzieć się, jak uzyskać tablicę [String] zwróconą z tej struktury, i wydaje się to złym miejscem na umieszczenie takiej metody.

 2
Author: Roger Worden,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-15 23:09:28

Zrobiłem to używając właściwości computed, która zwraca tablicę wszystkich wartości (dzięki temu postowi http://natecook.com/blog/2014/10/loopy-random-enum-ideas / ). jednak używa również int raw-values, ale nie muszę powtarzać wszystkich elementów wyliczenia w osobnej właściwości.

UPDATE Xcode 6.1 zmienił trochę sposób, jak uzyskać członek enum używając raw value, więc naprawiłem listing. Naprawiono również mały błąd z błędną pierwszą wartością Raw

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

    static var allSuits:[ValidSuits]{
        return Array(
            SequenceOf {
                () -> GeneratorOf<ValidSuits> in
                var i=0
                return GeneratorOf<ValidSuits>{
                    return ValidSuits(rawValue: i++)
                }
            }
        )
    }
}
 2
Author: gleb vodovozov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-21 12:11:18

Podczas zajmowania się Swift 2.0 Oto moja sugestia:

Dodałem surowy Typ Do Suit enum

enum Suit: Int {

Wtedy:

struct Card {
    var rank: Rank
    var suit: Suit


    func fullDeck()-> [Card] {

        var deck = [Card]()

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

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

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

        return deck
    }
}
 2
Author: Aladin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-16 21:18:05

Dla enum reprezentujących Int

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

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

Nazwij to tak:

print(Filter.allValues)

Druki:

[0, 1, 2, 3, 4]


Dla enum reprezentujących String

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

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

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

Nazwij to

print(Filter.allValues)

Druki:

["wynagrodzenie", "doświadczenie", "Technologia", "niewykorzystany", "niewykorzystany wysoki koszt"]

 2
Author: Warif Akhand Rishi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-20 12:57:57

Inne rozwiązanie:

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

    static var count: Int {
        return 4   
    }

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

for i in 0..<Suit.count {
    print(Suit(index: i).rawValue)
}
 2
Author: Miguel Gallego,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-30 20:19:06

To jest dość stary post, z Swift 2.0. Są teraz lepsze rozwiązania, które wykorzystują nowsze funkcje swift 3.0: iteracja poprzez Enum w Swift 3.0

I na to pytanie jest rozwiązanie, które wykorzystuje nową funkcjonalność (Nie-jeszcze-wydany jak piszę ten edit) Swift 4.2: Jak uzyskać liczbę Swift enum?


Jest wiele dobrych rozwiązań w tym wątku i innych, jednak niektóre z nich są bardzo skomplikowane. Lubię uprość jak najwięcej. Oto rozwiązanie, które może, ale nie musi działać dla różnych potrzeb, ale myślę, że działa dobrze w większości przypadków: {]}

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

Do iteracji:

for item in Number.allValues {
    print("number is: \(item)")
}
 2
Author: Abbey Jackson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-30 15:40:55

Oto metoda, której używam do iteracji enum i dostarczania wielu typów wartości z jednego enum

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

To jest wyjście:

Liczba Zero w języku francuskim: zéro, hiszpańskim: cero, japońskim: nuru
Liczba pierwsza w języku francuskim: un, hiszpańskim: uno, japońskim: ichi
Liczba druga w języku francuskim: deux, hiszpańskim: dos, japońskim: ni
Liczba trzecia w języku francuskim: trois, hiszpańskim: Tres, japońskim: san
Liczba czwarta w języku francuskim: quatre, hiszpańskim: cuatro, japońskim: shi
The number Pięć w języku francuskim: cinq, hiszpańskim: cinco, japońskim: go
Liczba szósta w języku francuskim: six, hiszpańskim: seis, japońskim: roku
Liczba siódma w języku francuskim: sept, hiszpańskim: siete, japońskim: shichi

Łącznie 8 przypadków

Siete po japońsku: shichi


Aktualizacja

Niedawno stworzyłem protokół do obsługi wyliczeń. Protokół wymaga enum o wartości int surowej:

protocol EnumIteration {

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

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

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

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

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

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

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

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

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

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

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

            if let eCase = enumCase {

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

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

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

        //last enumIndex (when enumCase == nil) is equal to the enum count
        return enumIndex
    }
}
 2
Author: MSimic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-08 15:26:43

Użyłem poniższej metody, założenie jest takie, że wiem, która jest ostatnią wartością w randze enum i wszystkie rangi mają wartości przyrostowe po asie

Wolę ten sposób, ponieważ jest czysty i mały, łatwy do zrozumienia

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

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

    return cards
}
 1
Author: Ninad Shah,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-06 08:20:03

Jest sprytny sposób, i frustrujący, jak to jest, ilustruje różnicę między dwoma różnymi rodzajami enum.

Spróbuj tego:

    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
    }

Układ polega na tym, że enum wspierane przez liczby (wartości surowe) jest niejawnie uporządkowane, podczas gdy enum, które nie jest wspierane przez liczby, jest jawnie niejawnie nieuporządkowane.

Np. gdy podamy liczby wartości enum, język jest na tyle sprytny, aby dowiedzieć się, w jakiej kolejności są liczby. Jeśli z drugiej strony nie jeśli spróbujemy iterować nad wartościami, język rzuca ręce w górę i mówi "tak, ale który z nich chcesz zrobić pierwszy???"

Inne języki, które mogą to zrobić (iteracja nad nieuporządkowanymi enumami), mogą być tymi samymi językami, w których wszystko jest "pod maską" w rzeczywistości mapą lub słownikiem, i możesz iterację nad kluczami mapy, niezależnie od tego, czy istnieje logiczna kolejność, czy nie.

Więc sztuką jest dostarczenie mu czegoś, co jest jawnie uporządkowane, w tym przypadku instancje suitów w tablicy w żądanej kolejności. Jak tylko dasz mu to, Swift jest jak " cóż, dlaczego nie powiedziałeś tak w pierwszej kolejności?"

Innym skrótem jest użycie operatora wymuszającego na funkcji fromRaw. Ilustruje to kolejną "wpadkę" na temat enum, że zakres możliwych wartości do przekazania jest często większy niż zakres enum. Na przykład, jeśli powiedzielibyśmy rangę.from (60) there wouldn ' t be a value returned, więc używamy opcjonalnej funkcji języka, i gdzie zaczniemy używać opcji, wymuszanie wkrótce nastąpi. (Lub na przemian if let konstrukcja, która nadal wydaje mi się nieco dziwna)

 1
Author: Rick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-07 13:15:33