Как перечислить перечисление со строковым типом?

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

Например, как я могу сделать что-то вроде:

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

Результирующий пример:


Люсьен
источник
В каком случае вы не знаете тип?
mtbennett
Вы правы, в данном случае это тип String.
Люсьен
В Свифте пока нет отражения ...
Sulthan
22
Разве не иронично, что их называют перечислениями, но они так мучительно раздражают перечислять в Swift?
Чарльтон Проватас
3
@CharltonProvatas Если бы это был единственный недостаток в Swift, я бы назвал это днем. Смотря на то, как много людей предлагают различные обходные пути для этого, я просто грыз свою клавиатуру.
qwerty_so

Ответы:

267

Swift 4.2+

Начиная с Swift 4.2 (с Xcode 10), просто добавьте соответствие протокола, чтобы CaseIterableполучить выгоду allCases. Чтобы добавить соответствие этого протокола, вам просто нужно написать куда-нибудь:

extension Suit: CaseIterable {}

Если перечисление принадлежит вам, вы можете указать соответствие непосредственно в декларации:

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

Затем следующий код выведет все возможные значения:

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

Совместимость с более ранними версиями Swift (3.x и 4.x)

Если вам требуется поддержка Swift 3.x или 4.0, вы можете имитировать реализацию 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
Кер
источник
@DmitryPetukhov Я был бы рад помочь, но: (1) вы уверены, что получили последнюю версию кода? (некоторый сбой был исправлен месяц назад) и (2), пожалуйста, укажите MCVE вашего пользовательского типа, который может воспроизвести сбой, и вашу версию Xcode.
Cœur,
Это прекрасно работает для отладочных сборок, но как только я создаю релиз и загружаю его в TestFlight, происходит сбой. Apple как-то это зачищает?
Дэниел Вуд
1
Кажется, у вашей версии есть один положительный момент по сравнению со встроенной версией CaseIterator. Я могу расширить перечисления, которые определены в другом файле с вашей версией. Если вы сделаете allCases в расширении общедоступным, вы также можете расширить перечисления, определенные в разных платформах.
adamfowlerphoto
1
@CyberMew Я обновил ответ, чтобы уточнить его. Упоминания о CaseIterable в книге Swift и примечаниях к выпуску Xcode 10 приведены после моего ответа, и они использовали упрощенные примеры, в которых перечисление не поддерживается String, в отличие от существующего вопроса переполнения стека.
Кер
1
Я хотел бы подчеркнуть важность "# if! Swift (> = 4.2)". Если вы написали свой код до swift 4.2 и забыли удалить приведенный ниже код «# if! Swift (> = 4.2)», когда вы используете Xcode Version 11.4 для локальной сборки на своем тестовом устройстве, все будет хорошо. Но когда ваше приложение загружается из магазина приложений или тестового полета, этот фрагмент кода может привести к сбою вашего приложения. Такую ошибку очень сложно обнаружить или отладить.
Оливер Чжан
524

Этот пост актуален здесь https://www.swift-studies.com/blog/2014/6/10/enumerating-enums-in-swift

По сути, предлагаемое решение

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

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

for category in ProductCategory.allValues{
     //Do something
}
rougeExciter
источник
188
Хорошо, но ... вы должны ввести свои элементы перечисления дважды - один раз для перечисления, один раз для allValues. Не так элегантно, как хотелось бы.
Джей Имерман
2
Согласитесь с «но» ... однако, как указано в статье, возможно, существует проблема, заключающаяся в том, что перечисление действительно является множеством и, следовательно, неупорядоченным ... обратите внимание ... случаи порядка, определенные в, не будут плохим началом!
rougeExciter
3
В Java компилятор сделает это за вас, возможно, Swift 2.0 сделает то же самое. В частности, в Java все перечисления получают метод description (toString в Java), который дает String в качестве имен случаев (Washers, ...), и автоматически создается набор случаев. Java также дает вам позиционную индексацию. Как я уже сказал, возможно Swift 2.0.
Говард Ловатт
1
В идеале у вас должно быть что-то похожее на реализацию c #, в которой вы можете сделать это, Enum.Values(typeof(FooEnum))но оно будет представлено как метод расширения (например, map или redu). FooEnum.values() :: values(EnumType -> [EnumType])
Родригель
1
В конце статьи хорошо сказано, что каждое значение перечисления находится в массиве allValues ​​с модульным тестом. Тем не менее, я все еще вижу, как кто-то добавляет больше элементов, но не применяет их в модульном тесте, который все еще оставляет нас в начале, не зная точно, что все значения перечисления хранятся в allValues.
DonnaLea
278

Я сделал функцию полезности iterateEnum()для итерации случаев для произвольных enumтипов.

Вот пример использования:

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

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

Какие выводы:

♠
♥
♦
♣

Но это только для целей отладки или тестирования : это зависит от недокументированного поведения компилятора Swift1.1, поэтому используйте его на свой страх и риск.

Вот код:

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

Основная идея:

  • Представление в памяти enum, исключая enums со связанными типами, является просто индексом случаев, когда количество случаев равно 2...256, равно UInt8, когда 257...65536, это UInt16и так далее. Таким образом, это может быть unsafeBitcastиз соответствующих целых типов без знака.
  • .hashValue значений перечисления совпадает с индексом регистра.
  • .hashValueо перечислении значения bitcasted от недопустимого индекса 0.

Пересмотрен для Swift2 и реализован идеи кастинга из ответа @ 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
    }
}

Пересмотрен для 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
    }
}

Пересмотрен для 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
    }
}
Ринтаро
источник
20
Удивительный и единственный ответ, который отвечает на вопрос! Но да ... не буду трогать это! +1 за усилие!
dotToString
Я только что опубликовал свой ответ, который работает в основном таким же образом (только позже увидел этот ответ). Он использует Swift 2.0 beta 6 и современные функции языка.
Каметриксом
2
Версия Swift 3 работает хорошо. Просто нужно немного изменить использование: для f в iterateEnum (Suit.self) {print (f.rawValue)}
Джин Де Лиза,
16
+1 это довольно блестяще. Это также, ИМХО, слишком умный в использовании, о чем свидетельствует существенный сбой при каждом значительном изменении версии Swift. К чести автора, версия Swift 3 была сделана за месяц до того, как Swift 3 вышел из бета-версии ... Если вы собираетесь взять этот ответ и изучить все это withUnsafePointer withMemoryReboundи pointeeпрочее, то используйте это во что бы то ни стало. В противном случае я бы избежал этого.
Дэн Розенстарк
5
Я просто хочу добавить, что теперь это не работает в swift 4, но только в linux, так что +1 к комментариям выше, что это слишком умно для использования.
Эндрю Пламмер
130

Другие решения работают, но все они делают предположения, например, о количестве возможных рангов и мастей, или о том, каким может быть первый и последний ранг. Правда, расположение колоды карт, вероятно, не изменится в обозримом будущем. В целом, однако, лучше писать код, который делает как можно меньше предположений. Мое решение:

Я добавил необработанный тип в Suitперечисление, так что я могу использовать Suit(rawValue:)для доступа к Suitслучаям:

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

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

Ниже приведена реализация метода Карда createDeck(). init(rawValue:)является неисправным инициализатором и возвращает необязательный. Развернув и проверив его значение в обоих операторах while, нет необходимости предполагать количество Rankили 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
    }
}

Вот как вызвать createDeckметод:

let card = Card(rank: Rank.Ace, suit: Suit.Clubs)
let deck = card.createDeck()
sdduursma
источник
12
Абсолютный ЛУЧШИЙ ответ, который я видел в различных темах на эту тему. Очень элегантно. Это работает с перечислениями типа Int, но мне интересно, как можно перебирать другие типы (строки, пользовательские типы и т. Д.).
Джей Имерман
3
Это определенно лучшее решение. Стоит отметить одну вещь. В примере, приведенном в книге, у него нет «case Spades = 1», как у sdduursma. Сначала я этого не понял. это один из вариантов, или вы можете просто использовать «var m = 0»
Джошуа Гуссен
10
Это делает предположение, что необработанные значения являются последовательными. Когда это не так, например, когда перечисление представляет флаги битовой маски, цикл завершается преждевременно.
Джим
1
Это решение предполагает, что вы можете изменить определение Suit. Вы можете в этом примере, но упражнение предназначалось для того, чтобы заставить вас работать с тем, что enumsвам дали, как если бы они были из внешнего источника.
Джош Дж.
1
Единственное, на что я хочу пожаловаться - это то, что я не могу назвать его статическим методом, но сначала должен создать объект карты.
16:54
76

Я наткнулся на биты и байты и создал расширение, которое позже обнаружил, что оно очень похоже на ответ @rintaro . Используется так:

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

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

Что примечательно, так это то, что его можно использовать для любого перечисления без соответствующих значений. Обратите внимание, что это не работает для перечислений, которые не имеют регистра.

Как и в ответе @rintaro , этот код использует базовое представление перечисления. Это представление не задокументировано и может измениться в будущем, что может его сломать. Я не рекомендую использовать это в производстве.

Код (Swift 2.2, Xcode 7.3.1, не работает на 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
            }
        }
    }
}

Код (Swift 3, Xcode 8.1, не работает на 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
            }
        }
    }
}

Понятия не имею, зачем мне это нужно typealias, но компилятор жалуется без этого.

Kametrixom
источник
10
Этот ответ даже лучше, чем мой ответ, особенно в
актерской
Но я думаю, что это работает только на байтовой среде?
Ринтаро
5
Xcode 8 beta 6 изменил это снова! Я получаю следующую ошибку: `'init' недоступен: используйте 'withMemoryRebound (to :acity: _)', чтобы временно просмотреть память как другой тип, совместимый с макетом.
Confused
1
@ConfusedVorlon: см. Ответ выше @Rintaro: заменить withUnsafePointerpointee}наwithUnsafePointer(to: &i) { $0.withMemoryRebound(to: T.self, capacity: 1) { $0.pointee } }
Стефан
1
Похоже, что это больше не работает с Xcode 10. (я знаю, что это не потребуется в Swift 4.2), но при использовании Swift 4 (.1) в Xcode 10 этот код больше не работает (необработанное значение неравно)
benrudhart
26

Вы можете перебирать перечисление путем реализации ForwardIndexTypeпротокола.

ForwardIndexTypeПротокол требует , чтобы определить successor()функцию пошаговых элементов.

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

Итерация по открытому или закрытому диапазону ( ..<или ...) вызовет внутреннюю successor()функцию, которая позволит вам написать это:

// 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
источник
2
Я считаю, что это самый «правильный» ответ на вопрос, даже самый «элегантный» (дополнительный код, необходимый по сравнению с другими опциями здесь), учитывая результирующий синтаксис при использовании в пределах диапазона (синтаксис - это то, что я можно было бы ожидать, если бы перечисления, где перечислимых без обходных путей). Спасибо! Хотя стоит отметить, что если перегрузка оператора используется в другом месте, кроме successor () (что кажется заманчивым), то очевидно, что принудительное развертывание опасно. Кроме того, инфикс кажется ненужным ...?
iOS Gamer
Обновленный ответ, отражающий последние спецификации языка Swift
RndmTsk
Правильно определенный successor()метод (первый вариант) устраняет необходимость enumиметь связанный тип. +1
nhgrif
1
Но этот элегантный ответ не сработает для перечислений String, не так ли?
Али
Самое «правильное» / лучшее практическое решение! + 1-е изд
Сиу Чинг-Понг-Асука Кэндзи -
18

Эта проблема теперь намного проще. Вот мое решение Swift 4.2:

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

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

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

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

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

До 4.2:

Мне нравится это решение, которое я собрал после нахождения « Понимания списка в Swift ».

Он использует Int raw вместо Strings, но избегает ввода дважды, позволяет настраивать диапазоны и не жестко кодирует необработанные значения.

Это версия Swift 4 моего оригинального решения, но см. Улучшение 4.2 выше:

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
источник
О, теперь я вижу, что мой в основном тот же, что и у Сутеана Рутжаналарда.
Adazacom
1
На самом деле, мне понравилась ваша реализация лучше. Я думаю, что это понятнее! 1 голос На самом деле, большинство проголосовавших ответов слишком умны и наверняка сломаются в будущем. Ваш обещает некоторую стабильность в будущем.
Jvarela
17

В принципе, это можно сделать таким образом, если вы не используете необработанные присваивания значений для случаев 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
источник
7
Это хорошо, но это работает только для непрерывных целочисленных перечислений, начинающихся с 0
Robert
@ Роберт, как сказано в моем комментарии выше: «Вы не используете необработанные присваивания значений для случаев enum»
Alfa07
Да - не используйте необработанные значения, а также не устанавливайте базовый тип как int. В swift вам не нужен тип для перечисления, как в примере мастей. enum ItWontWorkForThisEnum {case a, b, c}
Роберт
Как это решит проблему, если кортеж связан со случаем перечисления?
rougeExciter
Вы не можете легко связать кортеж с перечислением.
nhgrif
13

Если вы дадите enum необработанное значение Int, это значительно упростит зацикливание.

Например, вы можете использовать anyGeneratorгенератор, который может перечислять ваши значения:

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

Тем не менее, это выглядит как довольно распространенный шаблон, не было бы неплохо, если бы мы могли сделать любой перечисляемый тип перечисления простым соответствием протоколу? Хорошо с Swift 2.0 и расширениями протокола, теперь мы можем!

Просто добавьте это в ваш проект:

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

Теперь каждый раз, когда вы создаете перечисление (если оно имеет необработанное значение Int), вы можете сделать его перечислимым, следуя протоколу:

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

Если ваши значения перечисления не начинаются с 0(по умолчанию), переопределите firstRawValueметод:

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

Окончательный класс костюма, включая замену simpleDescriptionс более стандартным CustomStringConvertible протоколом , будет выглядеть следующим образом :

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:

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
    }
}
Senseful
источник
nextIndex ++ будет удален в swift 3. Что вы предлагаете в качестве замены - var nextIndex = firstRawValue () return anyGenerator {Self (rawValue: nextIndex ++)}
Avi
Догадаться. defer {nextIndex + = 1} return AnyGenerator {Self (rawValue: nextIndex)}
Ави
12

Обновлено до 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
        }
    }
}

Обновлен код до формы Swift 2.2 @ Kametrixom's answer

Для Swift 3.0+ (большое спасибо @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
источник
@silvansky не могли бы вы объяснить, что вы имеете в виду?
ale_stro
Ой, извините, я перепроверил, и была ошибка на игровой площадке: в реальном проекте этот код работает как положено, спасибо! =)
Сильванский
1
Отличное решение! Также отлично работает на Swift 3 с минимальными изменениями («AnyGenerator» переименован в «AnyIterator» и «.memory» переименован в «.pointee»).
Филипп
9

Решение Swift 5:

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

// access cases like this:

for suitKey in Suit.allCases {
    print(suitKey)
}
JD Wooder
источник
7

Я обнаружил, что .allValuesмного делаю по всему коду. Я наконец нашел способ просто соответствовать Iteratableпротоколу и иметь 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

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

Назови это

print(Filter.allValues)

Печать:

[«Зарплата», «Опыт», «Технологии», «Неиспользованные», «Неиспользованные высокой стоимости»]


Старые версии

Для enumпредставления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 }
}

Назовите это так:

print(Filter.allValues)

Печать:

[0, 1, 2, 3, 4]


Для enumпредставления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"
        }
    }
}

Назови это

print(Filter.allValues)

Печать:

[«Зарплата», «Опыт», «Технологии», «Неиспользованные», «Неиспользованные высокой стоимости»]

Warif Akhand Rishi
источник
7

РЕДАКТИРОВАТЬ: Swift Evolution Proposal SE-0194 Производная коллекция Enum Cases предлагает поэтапное решение этой проблемы. Мы видим это в Swift 4.2 и новее. Предложение также указывает на некоторые обходные пути , аналогичные тем, которые уже упоминались здесь, но, тем не менее, было бы интересно посмотреть.

Я также сохраню свой оригинальный пост для полноты картины.


Это еще один подход, основанный на ответе @ Peymmankh, адаптированный к Swift 3 .

public protocol EnumCollection: Hashable {}

extension EnumCollection {

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

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

    return [S](retVal)
}
ff10
источник
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
    }
}

Как насчет этого?

Mossila
источник
Спасибо, все работает как мне нужно. Но есть ли возможность в замыкании карты получить значение не по индексу, а по имени?
Михаил
4

Вы можете попробовать перечислить, как это

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]
Картик Кумар
источник
1
Это много бесполезного кода! Это равносильно тому static var enumerate = [Mercury, Venus, Earth, Mars], что он является ответом на более низком уровне по сравнению с большинством проголосовавших ответов stackoverflow.com/a/24137319/1033581
Cœur,
@ Cœur этот ответ имеет важное преимущество использования компилятора, чтобы гарантировать, что вы не пропустите дело.
dchakarov
@ Cœur, который имеет ту же проблему, позволяющую вам сделать ошибку пользователя, т.е. компилятор не будет жаловаться, если вы напишите return [Mercury, Venus, Mars]вместоreturn [Mercury, Venus, Earth, Mars]
dchakarov
@dchakarov Я решил опубликовать улучшение как ответ, для ясности: stackoverflow.com/a/50409525/1033581
Cœur
@ Cœur Если в своем новом ответе вы замените оператор return на этот, return [.spades, .hearts, .clubs]компилятор ничего не скажет, а затем, когда вы попытаетесь использовать его в коде, вы поймете [TestApp.Suit.spades, TestApp.Suit.hearts, TestApp.Suit.clubs]- это было моей точкой зрения - если вы имеете дело с большим enum, и вы должны время от времени добавлять или удалять случаи, ваше решение подвержено ошибкам пропуска, в то время как текущий ответ, хотя и не является кратким, безопаснее.
dchakarov
4

В Swift 3, когда есть базовое перечисление rawValue, вы можете реализовать Strideableпротокол. Преимущества состоят в том, что массивы значений не создаются, как в некоторых других предложениях, и что работает стандартный цикл Swift «для», что создает приятный синтаксис.

// "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
источник
Ааа, именно то, что я искал, чтобы заменить ForwardIndexType. Теперь мои итерации выглядят хорошо на сайте использования ... просто надлежащим образом Swifty.
Эндрю Дункан
4

Это решение обеспечивает правильный баланс удобочитаемости и удобства обслуживания.

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()
Эндрю
источник
На мой взгляд, это лучшее решение. Когда я вижу быстрый код, в основном читаемость не лучше, чем у objc. Но это могло быть, если бы программисты уделяли больше внимания читателям своего кода. Их будущие я, например :)
Vilém Kurz
4

Извините, мой ответ был конкретным, как я использовал этот пост в том, что мне нужно было сделать. Для тех, кто наткнулся на этот вопрос, ищет способ найти случай в перечислении, вот способ сделать это (новое в Swift 2):

Редактировать: строчный camelCase теперь стандарт для значений перечисления Swift 3

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

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

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

Для тех, кто интересуется перечислением перечислений, ответы на этой странице, включающие статический var / let, содержащий массив всех значений перечисления, являются правильными. Последний пример кода Apple для tvOS содержит ту же самую технику.

При этом они должны встроить более удобный механизм в язык (Apple, вы слушаете?)!

Джин Лопарко
источник
3

Эксперимент был: ЭКСПЕРИМЕНТ

Добавьте метод к Карте, который создает полную колоду карт, с одной картой каждой комбинации ранга и масти.

Поэтому, не изменяя или не улучшая данный код, кроме добавления метода (и не используя материал, который еще не изучен), я придумал это решение:

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
источник
3

Вот метод, который я использую для итерации enumи предоставления нескольких типов значений из одного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"))")

Это вывод:

Число ноль по-французски: zéro, испанский: cero, japanese: nuru
Число по-французски: un, испанский: uno, japanese: ichi
Номер два по-французски: deux, испанский: dos, японский: ni
Номер три по-французски : trois, испанский: tres, japanese: san
Номер четыре на французском: quatre, испанский: cuatro, japanese: shi
Номер пять на французском: cinq, испанский: cinco, japanese: go
Номер шесть на французском: шесть, испанский: seis, японский: roku
Число семь на французском: сент, испанский: сите, японский: shichi

Всего 8 случаев

сиэтэ по японски: shichi


ОБНОВИТЬ

Недавно я создал протокол для обработки перечисления. Протокол требует перечисления с необработанным значением Int:

protocol EnumIteration {

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

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

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

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

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

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

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

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

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

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

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

            if let eCase = enumCase {

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

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

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

        //last enumIndex (when enumCase == nil) is equal to the enum count
        return enumIndex
    }
}
MSimic
источник
2

Это похоже на хак, но если вы используете необработанные значения, вы можете сделать что-то вроде этого

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

var suitIndex = 0  
while var suit = Suit.fromRaw(suitIndex++) {  
   ...  
}  
Kenh
источник
2

Пока имею дело с Swift 2.0вот моим предложением:

Я добавил необработанный тип в Suit enum

enum Suit: Int {

тогда:

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
источник
2

Как с @Kametrixom ответ здесь , я считаю , возвращая массив будет лучше , чем возвращение AnySequence, так как вы можете иметь доступ ко всему лакомство массива, такие как подсчет, и т.д.

Вот переписать:

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
источник
2

Другое решение:

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

Это довольно старый пост из Swift 2.0. Теперь здесь есть несколько лучших решений, использующих новые функции swift 3.0: перебор Enum в Swift 3.0

И в этом вопросе есть решение, которое использует новую функцию (еще не выпущенную, когда я пишу это редактирование) Swift 4.2: Как я могу получить счет перечисления Swift?


В этой ветке есть много хороших решений, но некоторые из них очень сложны. Мне нравится максимально упрощать. Вот решение, которое может или не может работать для разных нужд, но я думаю, что оно работает хорошо в большинстве случаев:

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

Итерировать:

for item in Number.allValues {
    print("number is: \(item)")
}
Аббатство джексон
источник
1
Это похоже на большую работу, специфичную для каждого созданного вами перечисления - я не уверен, что return [Number.One.rawValue, Number.Two.rawValue, ...] не чище, в этом случае ,
Скотт Остин
Это довольно старый пост из Swift 2.0. В настоящее время существуют некоторые более эффективные решения, в которых используются новые функции swift 3.0: stackoverflow.com/questions/41352594/… И в этом вопросе есть решение, в котором используется новая функция (еще не выпущенная, когда я пишу это изменение ) Swift 4.2: stackoverflow.com/questions/27094878/…
аббатство Джексон,
2

Перечни есть toRaw()и fromRaw()методы. Поэтому, если ваше необработанное значение равно Int, вы можете перебирать от первого до последнего 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()
    }
}

Одна проблема заключается в том, что вам нужно проверить дополнительные значения перед запуском simpleDescriptionметода, поэтому convertedSuitсначала мы устанавливаем наше значение, а затем устанавливаем константуconvertedSuit.simpleDescription()

Адриан Харрис Краун
источник
2
Первоначальный вопрос был о enum типа String, а не Int
cynistersix
2

Вот мой предложенный подход. Это не совсем удовлетворительно (я очень новичок в Swift и ООП!), Но, возможно, кто-то может улучшить это. Идея состоит в том, чтобы каждое перечисление предоставляло собственную информацию о диапазоне .firstи .lastсвойствах. Он добавляет всего две строки кода к каждому перечислению: все еще немного жестко, но, по крайней мере, он не дублирует весь набор. Это требует изменения Suitперечисления, чтобы быть Int, какRank enum, а не нетипизированным.

Вместо того, чтобы отражать все решение, вот код, который я добавил в .enum, где-то после операторов case ( Suitenum похож):

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

и цикл, который я использовал для построения колоды в виде массива String. (В определении проблемы не указано, как должна быть структурирована колода.)

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
}

Это неудовлетворительно, потому что свойства связаны с элементом, а не с перечислением. Но это добавляет ясности в циклы for. Я бы хотел это сказать Rank.firstвместоRank.Ace.first . Это работает (с любым элементом), но это некрасиво. Может кто-нибудь показать, как поднять это до уровня enum?

И чтобы это сработало, я поднял createDeckметод из структуры Card. Я не мог понять, как получить массив [String], возвращаемый из этой структуры, и это все равно кажется плохим местом для размещения такого метода.

Роджер Уорден
источник
2

Я сделал это, используя свойство computed, которое возвращает массив всех значений (благодаря этому посту http://natecook.com/blog/2014/10/loopy-random-enum-ideas/ ). Однако он также использует int raw-values, но мне не нужно повторять все члены перечисления в отдельном свойстве.

ОБНОВЛЕНИЕ Xcode 6.1 немного изменил способ получения члена enum rawValue, поэтому я исправил листинг. Также исправлена ​​небольшая ошибка с неправильным первым 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++)
                }
            }
        )
    }
}
глеб водовозов
источник
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
    }
}

Исходя из ответа Рика: это в 5 раз быстрее

Джон Люч-Зоррилья
источник
Добавление Countслучая нарушит исчерпывающие switchреализации и CaseIterableсоответствие.
Cœur