如何枚举具有字符串类型的枚举?

时间:2021-12-02 16:36:16
enum Suit: String {
    case spades = "♠"
    case hearts = "♥"
    case diamonds = "♦"
    case clubs = "♣"
}

For example, how can I do something like:

例如,我怎么做:

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

Resulting example:

结果示例:

♠
♥
♦
♣

40 个解决方案

#1


474  

This post is relevant here https://www.swift-studies.com/blog/2014/6/10/enumerating-enums-in-swift

这篇文章与此相关,https://www.swift- studies.com/blog/2014/6/10/enumerating-enums-inswift。

Essentially the proposed solution is

本质上,建议的解决方案是。

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

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

for category in ProductCategory.allValues{
     //Do something
}

#2


263  

I made a utility function iterateEnum() for iterating cases for arbitrary enum types.

我创建了一个实用函数iterateEnum(),用于迭代任意枚举类型的情况。

Here is the example usage:

下面是例子的用法:

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

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

outputs:

输出:

♠
♥
♦
♣

But, this is only for debug or test purpose: This relies on several undocumented current(Swift1.1) compiler behaviors. So, use it at your own risk :)

但是,这仅用于调试或测试目的:这依赖于几个未归档的流(Swift1.1)编译器行为。所以,在你自己的风险中使用它:)

Here is the code:

这是代码:

func iterateEnum<T: Hashable>(_: T.Type) -> GeneratorOf<T> {
    var cast: (Int -> T)!
    switch sizeof(T) {
    case 0: return GeneratorOf(GeneratorOfOne(unsafeBitCast((), T.self)))
    case 1: cast = { unsafeBitCast(UInt8(truncatingBitPattern: $0), T.self) }
    case 2: cast = { unsafeBitCast(UInt16(truncatingBitPattern: $0), T.self) }
    case 4: cast = { unsafeBitCast(UInt32(truncatingBitPattern: $0), T.self) }
    case 8: cast = { unsafeBitCast(UInt64($0), T.self) }
    default: fatalError("cannot be here")
    }

    var i = 0
    return GeneratorOf {
        let next = cast(i)
        return next.hashValue == i++ ? next : nil
    }
}

The underlying idea is:

底层的想法是:

  • Memory representation of enum - excluding enums with associated types - is just a index of cases, when the count of the cases is 2...256, it's identical to UInt8, when 257...65536, it's UInt16 and so on. So, it can be unsafeBitcast from corresponding unsigned integer types.
  • 枚举的内存表示——不包括与相关类型的枚举——只是一个案例的索引,当案例的计数为2时……256,和UInt8一样,257…65536,是UInt16,等等。因此,它可以由对应的无符号整数类型的unsafeBitcast。
  • .hashValue of enum values is the same as the index of the case.
  • . enum值的hashvalue与case的索引相同。
  • .hashValue of enum values bitcasted from invalid index is 0
  • 从无效索引中提取的枚举值为0。

ADDED:

补充道:

Revised for Swift2 and implemented casting ideas from @Kametrixom's answer

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

ADDED: Revised for Swift3

补充道: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
    }
}

ADDED: Revised for Swift3.0.1

补充道: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
    }
}

#3


123  

The other solutions work but they all make assumptions of for example the number of possible ranks and suits, or what the first and last rank may be. True, the layout of a deck of cards probably isn't going to change much in the foreseeable future. In general, however, it's neater to write code which makes as little assumptions as possible. My solution:

其他的解决方案是有效的,但是他们都假设了可能的等级和诉讼的数量,或者第一个和最后一个等级。的确,一副纸牌的布局在可预见的未来可能不会有太大的变化。但是,一般来说,编写代码时要尽量少做假设。我的解决方案:

I've added a raw type to the suit enum, so I can use Suit(rawValue:) to access the Suit cases:

我在suit enum中添加了一个raw类型,所以我可以使用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)
        }
    }
}

Below the implementation of Card's createDeck() method. init(rawValue:) is a failable initializer and returns an optional. By unwrapping and checking it's value in both while statements, there's no need to assume the number of Rank or Suit cases:

下面是卡的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
    }
}

Here is how to call the createDeck method:

下面是如何调用createDeck方法:

let card = Card(rank: Rank.Ace, suit: Suit.Clubs)
let deck = card.createDeck()

#4


71  

The second answer that really works

So I stumbled around in the bits and bytes and created an extension (that I later found out works very similar to @rintaro's answer). It's usable like this:

因此,我在字节和字节中发现了一些问题,并创建了一个扩展(后来我发现它与@rintaro的答案非常相似)。它是可用的:

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

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

Remarkable is that it's usable on any enum (without associated values). Note that this doesn't work for enums that have no cases.

值得注意的是,它在任何枚举中都可用(没有相关的值)。请注意,这对没有案例的enums不起作用。

Disclaimer

As with @rintaro's answer, this code uses the underlying representation of an enum. This representation isn't documented and might change in the future, which would break it -> I don't recommend the usage of this in production.

与@rintaro的答案一样,该代码使用枚举的底层表示。这个表示没有文档化,将来可能会发生变化,这会破坏它——>我不建议在生产中使用它。

EDIT: It's been about a year and this still works.

编辑:已经有一年了,这仍然有效。

Code (Swift 2.2, Xcode 7.3.1)

protocol EnumCollection : Hashable {}
extension EnumCollection {
    static func cases() -> AnySequence<Self> {
        typealias S = Self
        return AnySequence { () -> AnyGenerator<S> in
            var raw = 0
            return AnyGenerator {
                let current : Self = withUnsafePointer(&raw) { UnsafePointer($0).memory }
                guard current.hashValue == raw else { return nil }
                raw += 1
                return current
            }
        }
    }
}

Code (Swift 3, Xcode 8.1)

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

(I have no idea why I need the typealias, but the compiler complains without it)

(我不知道为什么我需要这种类型的别名,但是编译器抱怨没有它)

(I made big modification to this answer, look at edits for past versions)

(我对这个答案做了很大的修改,查看过去版本的编辑)

#5


25  

You could iterate through an enum by implementing the ForwardIndexType protocol.

您可以通过实现ForwardIndexType协议来遍历枚举。

The ForwardIndexType protocol requires you to define a successor() function to step through the elements.

ForwardIndexType协议要求您定义一个后续()函数来遍历元素。

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

Iterating over an open or closed range (..< or ...) will internally call the successor() function which allows you to write this:

在打开或关闭的范围内迭代(.. <或…)将内部调用后续()函数,该函数允许您写入:< p>

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

#6


17  

In principle it is possible to do it this way assuming that you don't use raw values assignment for enum's cases:

原则上,这样做是有可能的,假设你不为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())")
}

#7


13  

Updated to Swift 2.2+

更新为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
        }
    }
}

it's updated code to Swift 2.2 form @Kametrixom's answer

它的更新代码为Swift 2.2表单@Kametrixom的答案。

For Swift 3.0+ (many thanks to @Philip)

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

#8


13  

If you give the enum a raw Int value it will make looping much easier.

如果您给enum一个原始的Int值,它将使循环更容易。

For example, you can use anyGenerator to get a generator that can enumerate across your values:

例如,您可以使用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())

However, this looks like a fairly common pattern, wouldn't it be nice if we could make any enum type enumerable by simply conforming to a protocol? Well with Swift 2.0 and protocol extensions, now we can!

然而,这看起来是一个相当普遍的模式,如果我们可以通过简单地遵循一个协议来创建枚举类型,这不是很好吗?现在我们可以使用Swift 2.0和协议扩展了!

Simply add this to your project:

只需将其添加到项目中:

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

Now any time you create an enum (so long as it has an Int raw value), you can make it enumerable by conforming to the protocol:

现在,只要您创建一个enum(只要它有一个Int原始值),您就可以通过遵从协议来让它枚举:

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

If your enum values don't start with 0 (the default), override the firstRawValue method:

如果枚举值不以0开始(默认值),请重写fir斯特瑞值方法:

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

The final Suit class, including replacing simpleDescription with the more standard CustomStringConvertible protocol, will look like this:

最终的Suit类,包括用更标准的CustomStringConvertible协议替换simpleDescription,将会是这样:

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 syntax:

斯威夫特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
    }
}

#9


13  

Swift 4.2+

Starting with Swift 4.2 (with Xcode 10.0?), just add protocol conformance to CaseIterable to benefit from allCases:

从Swift 4.2开始(使用Xcode 10.0?),只需添加协议一致性,以从所有案例中获益:

extension Suit: CaseIterable {}

Then this will print all possible values:

然后这将打印所有可能的值:

Suit.allCases.forEach {
    print(suit.rawValue)
}

Compatibility with earlier Swift versions (3.x and 4.x)

Just mimic the Swift 4.2 implementation:

模仿Swift 4.2实现:

#if swift(>=4.2)
#else
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
            return AnyIterator {
                let current = withUnsafeBytes(of: &raw) { $0.load(as: Self.self) }
                guard current.hashValue == raw else {
                    return nil
                }
                raw += 1
                return current
            }
        })
    }
}
#endif

#10


9  

I like this solution which I put together after finding this page: List comprehension in Swift

我喜欢这个解决方案,我在找到这一页后放在一起:列表理解迅速。

It uses Int raws instead of Strings but it avoids typing twice, it allows customizing the ranges, and doesn't hard code raw values.

它使用的是Int raws而不是字符串,但是它避免了两次输入,它允许自定义范围,并且不硬编码原始值。

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
}

#11


7  

I found myself doing .allValues alot throughout my code. I finally figured out a way to simply conform to an Iteratable protocol and have an rawValues() method.

我发现自己在做。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"]

#12


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

How about this?

这个怎么样?

#13


4  

Sorry, my answer was specific to how I used this post in what I needed to do. For those who stumble upon this question, looking for a way to find a case within an enum, this is the way to do it (new in Swift 2):

对不起,我的回答是关于我是如何在我需要做的事情上使用这个帖子的。对于那些偶然发现这个问题的人,寻找一个在enum中找到一个案例的方法,这是一种方法(Swift 2):

Edit: lowercase camelCase is now the standard for Swift 3 enum values

编辑:小写的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)
        }
    }

For those wondering about the enumerating on an enum, the answers given on this page that include a static var/let containing an array of all enum values are correct. The latest Apple example code for tvOS contains this exact same technique.

对于那些对枚举枚举感到疑惑的人来说,在这个页面上给出的答案包括一个包含所有枚举值数组的静态var/let。最新的tvOS示例代码包含了完全相同的技术。

That being said, they should build a more convenient mechanism into the language (Apple, are you listening?)!

也就是说,他们应该建立一种更方便的语言机制(苹果,你在听吗?)

#14


4  

In Swift 3, when the underlying enum has {rawValue}s, you could implement the {Strideable} protocol. The advantages are that no arrays of values are created like in some other suggestions, and that the standard Swift "for i in ..." statement works, which makes for nice syntax.

在Swift 3中,当底层枚举有{rawValue}时,您可以实现{Strideable}协议。优点是,没有像其他建议那样创建值数组,并且标准Swift“for i in…”语句工作,这使得语法很好。

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

#15


4  

EDIT: Swift Evolution Proposal SE-0194 Derived Collection of Enum Cases proposes a level headed solution to this problem. We will see it in Swift 4.2 and Swift 5. The proposal also points out to some workarounds that are similar to some already mentioned here but it might be interesting to see nevertheless.

编辑:Swift Evolution提案SE-0194派生的枚举案例提出了一个针对这个问题的层次解决方案。我们将在Swift 4.2和Swift 5中看到它。该提案还指出了一些类似于这里已经提到的一些变通方法,但这可能会很有趣。

I will also keep my original post for completeness sake.

为了完整起见,我也会保留原来的帖子。


This is yet another approach based on @Peymmankh's answer, adapted to Swift 3.

这是另一种基于@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)
  }
}

#16


4  

This is what I ended up going with; I think it strikes the right balance of readability and maintainability.

这就是我最后要讲的;我认为它达到了可读性和可维护性的正确平衡。

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

#17


3  

The experiment was: EXPERIMENT

实验:实验

Add a method to Card that creates a full deck of cards, with one card of each combination of rank and suit.

在卡片上添加一种方法,可以创建一副扑克牌,每一副牌都有等级和套装的组合。

So without modifying or enhancing the given code other than adding the method (and without using stuff that hasn't been taught yet), I came up with this solution:

因此,如果不修改或增强给定的代码,而不是添加方法(并且不使用尚未被教过的东西),我就找到了这个解决方案:

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

#18


3  

As with @Kametrixom answer here I believe returning an array would be better than returning AnySequence, since you can have access to all of Array's goodies such as count, etc.

与@Kametrixom的回答一样,我认为返回一个数组比返回任何序列都要好,因为您可以访问所有数组的优点,比如count等。

Here's the re-write:

重写:

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

#19


2  

Enums have toRaw() and fromRaw() methods so if your raw value is an Int, you can iterate from the first to last enum:

Enums有toRaw()和fromRaw()方法,因此如果您的原始值是Int,您可以从第一次到最后一次枚举迭代:

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

One gotcha is that you need to test for optional values before running the simpleDescription method, so we set convertedSuit to our value first and then set a constant to convertedSuit.simpleDescription()

一个问题是,在运行simpleDescription方法之前,您需要对可选值进行测试,因此我们首先将convertedSuit设置为我们的值,然后将一个常量设置为convertedSuit.simpleDescription()

#20


2  

This seems like a hack but if you use raw values you can do something like this

这看起来像一个hack,但是如果你使用原始值,你可以做类似的事情。

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

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

#21


2  

Here's my suggested approach. It's not completely satisfactory (I'm very new to Swift and OOP!) but maybe someone can refine it. The idea is to have each enum provide its own range information as .first and .last properties. It adds just two lines of code to each enum: still a bit hard-coded, but at least it's not duplicating the whole set. It does require modifying the Suit enum to be an Int like the Rank enum is, instead of untyped.

这是我的建议的方法。这并不完全令人满意(我对Swift和OOP非常熟悉),但也许有人可以改进它。这个想法是让每个enum提供它自己的范围信息。它只向每个enum添加了两行代码:仍然有一些硬编码,但至少它没有复制整个集合。它确实需要修改Suit enum,使它像Rank enum一样,而不是没有类型的。

Rather than echo the whole solution, here's the code I added to the Rank enum, somewhere after the case statements (Suit enum is similar):

与其呼应整个解决方案,下面是我添加到Rank enum的代码,在case语句之后的某个地方(Suit enum类似):

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

and the loop I used to build the deck as an array of String. (The problem definition did not state how the deck was to be structured.)

而我用来构建deck的循环是字符串数组。(问题的定义并没有说明该平台的结构。)

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
}

It's unsatisfactory because the properties are associated to an element rather than to the enum. But it does add clarity to the 'for' loops. I'd like it to say Rank.first instead of Rank.Ace.first. It works (with any element), but it's ugly. Can someone show how to elevate that to the enum level?

这并不令人满意,因为属性与元素相关联,而不是与enum相关。但它确实为“for”循环增加了清晰度。我希望它是Rank。第一,而不是Rank.Ace.first。它可以工作(任何元素),但它很丑。有人能展示如何将其提升到enum级别吗?

And to make it work, I lifted the createDeck method out of the Card struct... could not figure out how to get a [String] array returned from that struct, and that seems a bad place to put such a method anyway.

为了使它有效,我从卡片结构中取出createDeck方法…不知道如何从该结构中返回一个[String]数组,这似乎是放置这种方法的一个坏地方。

#22


2  

I did it using computed property, which returns the array of all values (thanks to this post http://natecook.com/blog/2014/10/loopy-random-enum-ideas/). However it also uses int raw-values, but I don't need to repeat all members of enumeration in separate property.

我使用计算属性来完成它,它返回所有值的数组(感谢这篇文章http://natecook.com/blog/2014/10/loopy-random-ideas/)。但是,它也使用int raw-values,但是我不需要在单独的属性中重复所有枚举成员。

UPDATE Xcode 6.1 changed a bit a way how to get enum member using raw value, so I fixed listing. Also fixed small error with wrong first raw value

更新Xcode 6.1更改了如何使用原始值获取enum成员的方式,所以我将其固定在了清单上。还用错误的第一个原始值修正小错误。

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

#23


2  

While dealing with Swift 2.0 here is my suggestion:

在处理Swift 2.0的同时,我的建议是:

I have added the raw type to Suit enum

我已经将原始类型添加到Suit enum。

enum Suit: Int {

then:

然后:

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

#24


2  

There are lots of good solutions in this thread and others however some of them are very complicated. I like to simplify as much as possible. Here is a solution which may or may not work for different needs but I think it works well in most cases:

在这个线程中有很多很好的解决方案,但是其中一些很复杂。我喜欢尽可能地简化。这里有一个解决方案,它可能适用于不同的需求,但我认为它在大多数情况下都适用:

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

To iterate:

迭代:

for item in Number.allValues {
    print("number is: \(item)")
}

#25


2  

For enum representing 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 }
}

Call it like this:

叫它是这样的:

print(Filter.allValues)

Prints:

打印:

[0, 1, 2, 3, 4]

[0,1,2,3,4]


For enum representing 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"
        }
    }
}

Call it

叫它

print(Filter.allValues)

Prints:

打印:

["Salary", "Experience", "Technology", "Unutilized", "Unutilized High Value"]

[“工资”、“经验”、“技术”、“未利用”、“未利用的高价值”]

#26


2  

Another solution:

另一个解决方案:

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

#27


2  

You can try to enumerate like this

你可以试着这样列举。

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]

#28


2  

Here a method I use to both iterate an enum, and provide multiple values types from one 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"))")

This is the output:

这是输出:

The number Zero in french: zéro, spanish: cero, japanese: nuru
The number One in french: un, spanish: uno, japanese: ichi
The number Two in french: deux, spanish: dos, japanese: ni
The number Three in french: trois, spanish: tres, japanese: san
The number Four in french: quatre, spanish: cuatro, japanese: shi
The number Five in french: cinq, spanish: cinco, japanese: go
The number Six in french: six, spanish: seis, japanese: roku
The number Seven in french: sept, spanish: siete, japanese: shichi

法国零数量:0,西班牙:大马鲛,日本:nuru数量一个在法国:联合国,西班牙:uno,日本:阿2号在法国:两个,西班牙:dos,日本:倪法国的3号:三,西班牙:非常,日本:圣法国的4号:四点,西班牙:四弦吉他,日本:“法国的5号:五点,西班牙:五、日本:去法国的6号:6、西班牙:检波器,日本:roku法国7数量:9月,西班牙:siete,日本:shichi

Total of 8 cases

共有8例

siete in japanese: shichi

日本:siete shichi

#29


1  

I have used the below method, the assumption is that I know which is the last value in the Rank enum and all the ranks have incremental values after Ace

我使用了下面的方法,假设是我知道哪个是Rank enum的最后一个值,并且所有的秩都有递增的值。

I prefer this way as it is clean and small, easy to understand

我喜欢这种方式,因为它干净、小巧,容易理解。

 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
}

#30


1  

There is a clever way, and frustrating as it is it illustrates the difference between the two different kinds of enums.

有一种聪明的方法,令人沮丧的是它说明了两种不同的枚举之间的区别。

Try this:

试试这个:

    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
    }

The deal is that an enum backed by numbers (raw values) is implicitly explicitly ordered, whereas an enum that isn't backed by numbers is explicitly implicitly unordered.

该协议是一个由数字(原始值)支持的枚举,它是隐式显式有序的,而没有数字支持的枚举是显式隐式无序的。

E.g. when we give the enum values numbers, the language is cunning enough to figure out what order the numbers are in. If on the other hand we don't give it any ordering, when we try to iterate over the values the language throws its hands up in the air and goes "yes, but which one do you want to go first???"

当我们给枚举值编号时,语言足够狡猾,可以找出数字的顺序。另一方面,如果我们不给它任何排序,当我们试图迭代这些值时,语言会把它的手举到空中,然后说,“是的,但是你想先去哪一个呢?”

Other languages which can do this (iterating over unordered enums) might be the same languages where everything is 'under the hood' actually a map or dictionary, and you can iterate over the keys of a map, whether there's any logical ordering or not.

其他可以这样做的语言(遍历无序的枚举)可能是相同的语言,所有的东西都是“底层”的,实际上是一个映射或字典,您可以遍历映射的键,不管是否有逻辑排序。

So the trick is to provide it with something that is explicitly ordered, in this case instances of the suits in an array in the order we want. As soon as you give it that, Swift is like "well why didn't you say so in the first place?"

关键是要提供一个明确有序的东西,在这个例子中,在数组中的实例中,我们想要的顺序。一旦你给出了答案,斯威夫特就会说:“你为什么一开始就不这么说呢?”

The other shorthand trick is to use the forcing operator on the fromRaw function. This illustrates another 'gotcha' about enums, that the range of possible values to pass in is often larger than the range of enums. For instance if we said Rank.fromRaw(60) there wouldn't be a value returned, so we're using the optional feature of the language, and where we start using optionals, forcing will soon follow. (Or alternately the if let construction which still seems a bit weird to me)

另一种简写技巧是在fromRaw函数上使用强制操作符。这说明了关于枚举的另一个“问题”,即传入的可能值的范围通常大于枚举的范围。例如,如果我们说Rank.fromRaw(60),就不会返回值,因此我们将使用该语言的可选特性,然后我们开始使用optionals,强制将很快跟随。(或者,如果让我觉得有点奇怪的话,让我来做吧。)

#1


474  

This post is relevant here https://www.swift-studies.com/blog/2014/6/10/enumerating-enums-in-swift

这篇文章与此相关,https://www.swift- studies.com/blog/2014/6/10/enumerating-enums-inswift。

Essentially the proposed solution is

本质上,建议的解决方案是。

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

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

for category in ProductCategory.allValues{
     //Do something
}

#2


263  

I made a utility function iterateEnum() for iterating cases for arbitrary enum types.

我创建了一个实用函数iterateEnum(),用于迭代任意枚举类型的情况。

Here is the example usage:

下面是例子的用法:

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

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

outputs:

输出:

♠
♥
♦
♣

But, this is only for debug or test purpose: This relies on several undocumented current(Swift1.1) compiler behaviors. So, use it at your own risk :)

但是,这仅用于调试或测试目的:这依赖于几个未归档的流(Swift1.1)编译器行为。所以,在你自己的风险中使用它:)

Here is the code:

这是代码:

func iterateEnum<T: Hashable>(_: T.Type) -> GeneratorOf<T> {
    var cast: (Int -> T)!
    switch sizeof(T) {
    case 0: return GeneratorOf(GeneratorOfOne(unsafeBitCast((), T.self)))
    case 1: cast = { unsafeBitCast(UInt8(truncatingBitPattern: $0), T.self) }
    case 2: cast = { unsafeBitCast(UInt16(truncatingBitPattern: $0), T.self) }
    case 4: cast = { unsafeBitCast(UInt32(truncatingBitPattern: $0), T.self) }
    case 8: cast = { unsafeBitCast(UInt64($0), T.self) }
    default: fatalError("cannot be here")
    }

    var i = 0
    return GeneratorOf {
        let next = cast(i)
        return next.hashValue == i++ ? next : nil
    }
}

The underlying idea is:

底层的想法是:

  • Memory representation of enum - excluding enums with associated types - is just a index of cases, when the count of the cases is 2...256, it's identical to UInt8, when 257...65536, it's UInt16 and so on. So, it can be unsafeBitcast from corresponding unsigned integer types.
  • 枚举的内存表示——不包括与相关类型的枚举——只是一个案例的索引,当案例的计数为2时……256,和UInt8一样,257…65536,是UInt16,等等。因此,它可以由对应的无符号整数类型的unsafeBitcast。
  • .hashValue of enum values is the same as the index of the case.
  • . enum值的hashvalue与case的索引相同。
  • .hashValue of enum values bitcasted from invalid index is 0
  • 从无效索引中提取的枚举值为0。

ADDED:

补充道:

Revised for Swift2 and implemented casting ideas from @Kametrixom's answer

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

ADDED: Revised for Swift3

补充道: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
    }
}

ADDED: Revised for Swift3.0.1

补充道: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
    }
}

#3


123  

The other solutions work but they all make assumptions of for example the number of possible ranks and suits, or what the first and last rank may be. True, the layout of a deck of cards probably isn't going to change much in the foreseeable future. In general, however, it's neater to write code which makes as little assumptions as possible. My solution:

其他的解决方案是有效的,但是他们都假设了可能的等级和诉讼的数量,或者第一个和最后一个等级。的确,一副纸牌的布局在可预见的未来可能不会有太大的变化。但是,一般来说,编写代码时要尽量少做假设。我的解决方案:

I've added a raw type to the suit enum, so I can use Suit(rawValue:) to access the Suit cases:

我在suit enum中添加了一个raw类型,所以我可以使用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)
        }
    }
}

Below the implementation of Card's createDeck() method. init(rawValue:) is a failable initializer and returns an optional. By unwrapping and checking it's value in both while statements, there's no need to assume the number of Rank or Suit cases:

下面是卡的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
    }
}

Here is how to call the createDeck method:

下面是如何调用createDeck方法:

let card = Card(rank: Rank.Ace, suit: Suit.Clubs)
let deck = card.createDeck()

#4


71  

The second answer that really works

So I stumbled around in the bits and bytes and created an extension (that I later found out works very similar to @rintaro's answer). It's usable like this:

因此,我在字节和字节中发现了一些问题,并创建了一个扩展(后来我发现它与@rintaro的答案非常相似)。它是可用的:

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

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

Remarkable is that it's usable on any enum (without associated values). Note that this doesn't work for enums that have no cases.

值得注意的是,它在任何枚举中都可用(没有相关的值)。请注意,这对没有案例的enums不起作用。

Disclaimer

As with @rintaro's answer, this code uses the underlying representation of an enum. This representation isn't documented and might change in the future, which would break it -> I don't recommend the usage of this in production.

与@rintaro的答案一样,该代码使用枚举的底层表示。这个表示没有文档化,将来可能会发生变化,这会破坏它——>我不建议在生产中使用它。

EDIT: It's been about a year and this still works.

编辑:已经有一年了,这仍然有效。

Code (Swift 2.2, Xcode 7.3.1)

protocol EnumCollection : Hashable {}
extension EnumCollection {
    static func cases() -> AnySequence<Self> {
        typealias S = Self
        return AnySequence { () -> AnyGenerator<S> in
            var raw = 0
            return AnyGenerator {
                let current : Self = withUnsafePointer(&raw) { UnsafePointer($0).memory }
                guard current.hashValue == raw else { return nil }
                raw += 1
                return current
            }
        }
    }
}

Code (Swift 3, Xcode 8.1)

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

(I have no idea why I need the typealias, but the compiler complains without it)

(我不知道为什么我需要这种类型的别名,但是编译器抱怨没有它)

(I made big modification to this answer, look at edits for past versions)

(我对这个答案做了很大的修改,查看过去版本的编辑)

#5


25  

You could iterate through an enum by implementing the ForwardIndexType protocol.

您可以通过实现ForwardIndexType协议来遍历枚举。

The ForwardIndexType protocol requires you to define a successor() function to step through the elements.

ForwardIndexType协议要求您定义一个后续()函数来遍历元素。

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

Iterating over an open or closed range (..< or ...) will internally call the successor() function which allows you to write this:

在打开或关闭的范围内迭代(.. <或…)将内部调用后续()函数,该函数允许您写入:< p>

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

#6


17  

In principle it is possible to do it this way assuming that you don't use raw values assignment for enum's cases:

原则上,这样做是有可能的,假设你不为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())")
}

#7


13  

Updated to Swift 2.2+

更新为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
        }
    }
}

it's updated code to Swift 2.2 form @Kametrixom's answer

它的更新代码为Swift 2.2表单@Kametrixom的答案。

For Swift 3.0+ (many thanks to @Philip)

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

#8


13  

If you give the enum a raw Int value it will make looping much easier.

如果您给enum一个原始的Int值,它将使循环更容易。

For example, you can use anyGenerator to get a generator that can enumerate across your values:

例如,您可以使用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())

However, this looks like a fairly common pattern, wouldn't it be nice if we could make any enum type enumerable by simply conforming to a protocol? Well with Swift 2.0 and protocol extensions, now we can!

然而,这看起来是一个相当普遍的模式,如果我们可以通过简单地遵循一个协议来创建枚举类型,这不是很好吗?现在我们可以使用Swift 2.0和协议扩展了!

Simply add this to your project:

只需将其添加到项目中:

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

Now any time you create an enum (so long as it has an Int raw value), you can make it enumerable by conforming to the protocol:

现在,只要您创建一个enum(只要它有一个Int原始值),您就可以通过遵从协议来让它枚举:

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

If your enum values don't start with 0 (the default), override the firstRawValue method:

如果枚举值不以0开始(默认值),请重写fir斯特瑞值方法:

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

The final Suit class, including replacing simpleDescription with the more standard CustomStringConvertible protocol, will look like this:

最终的Suit类,包括用更标准的CustomStringConvertible协议替换simpleDescription,将会是这样:

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 syntax:

斯威夫特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
    }
}

#9


13  

Swift 4.2+

Starting with Swift 4.2 (with Xcode 10.0?), just add protocol conformance to CaseIterable to benefit from allCases:

从Swift 4.2开始(使用Xcode 10.0?),只需添加协议一致性,以从所有案例中获益:

extension Suit: CaseIterable {}

Then this will print all possible values:

然后这将打印所有可能的值:

Suit.allCases.forEach {
    print(suit.rawValue)
}

Compatibility with earlier Swift versions (3.x and 4.x)

Just mimic the Swift 4.2 implementation:

模仿Swift 4.2实现:

#if swift(>=4.2)
#else
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
            return AnyIterator {
                let current = withUnsafeBytes(of: &raw) { $0.load(as: Self.self) }
                guard current.hashValue == raw else {
                    return nil
                }
                raw += 1
                return current
            }
        })
    }
}
#endif

#10


9  

I like this solution which I put together after finding this page: List comprehension in Swift

我喜欢这个解决方案,我在找到这一页后放在一起:列表理解迅速。

It uses Int raws instead of Strings but it avoids typing twice, it allows customizing the ranges, and doesn't hard code raw values.

它使用的是Int raws而不是字符串,但是它避免了两次输入,它允许自定义范围,并且不硬编码原始值。

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
}

#11


7  

I found myself doing .allValues alot throughout my code. I finally figured out a way to simply conform to an Iteratable protocol and have an rawValues() method.

我发现自己在做。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"]

#12


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

How about this?

这个怎么样?

#13


4  

Sorry, my answer was specific to how I used this post in what I needed to do. For those who stumble upon this question, looking for a way to find a case within an enum, this is the way to do it (new in Swift 2):

对不起,我的回答是关于我是如何在我需要做的事情上使用这个帖子的。对于那些偶然发现这个问题的人,寻找一个在enum中找到一个案例的方法,这是一种方法(Swift 2):

Edit: lowercase camelCase is now the standard for Swift 3 enum values

编辑:小写的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)
        }
    }

For those wondering about the enumerating on an enum, the answers given on this page that include a static var/let containing an array of all enum values are correct. The latest Apple example code for tvOS contains this exact same technique.

对于那些对枚举枚举感到疑惑的人来说,在这个页面上给出的答案包括一个包含所有枚举值数组的静态var/let。最新的tvOS示例代码包含了完全相同的技术。

That being said, they should build a more convenient mechanism into the language (Apple, are you listening?)!

也就是说,他们应该建立一种更方便的语言机制(苹果,你在听吗?)

#14


4  

In Swift 3, when the underlying enum has {rawValue}s, you could implement the {Strideable} protocol. The advantages are that no arrays of values are created like in some other suggestions, and that the standard Swift "for i in ..." statement works, which makes for nice syntax.

在Swift 3中,当底层枚举有{rawValue}时,您可以实现{Strideable}协议。优点是,没有像其他建议那样创建值数组,并且标准Swift“for i in…”语句工作,这使得语法很好。

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

#15


4  

EDIT: Swift Evolution Proposal SE-0194 Derived Collection of Enum Cases proposes a level headed solution to this problem. We will see it in Swift 4.2 and Swift 5. The proposal also points out to some workarounds that are similar to some already mentioned here but it might be interesting to see nevertheless.

编辑:Swift Evolution提案SE-0194派生的枚举案例提出了一个针对这个问题的层次解决方案。我们将在Swift 4.2和Swift 5中看到它。该提案还指出了一些类似于这里已经提到的一些变通方法,但这可能会很有趣。

I will also keep my original post for completeness sake.

为了完整起见,我也会保留原来的帖子。


This is yet another approach based on @Peymmankh's answer, adapted to Swift 3.

这是另一种基于@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)
  }
}

#16


4  

This is what I ended up going with; I think it strikes the right balance of readability and maintainability.

这就是我最后要讲的;我认为它达到了可读性和可维护性的正确平衡。

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

#17


3  

The experiment was: EXPERIMENT

实验:实验

Add a method to Card that creates a full deck of cards, with one card of each combination of rank and suit.

在卡片上添加一种方法,可以创建一副扑克牌,每一副牌都有等级和套装的组合。

So without modifying or enhancing the given code other than adding the method (and without using stuff that hasn't been taught yet), I came up with this solution:

因此,如果不修改或增强给定的代码,而不是添加方法(并且不使用尚未被教过的东西),我就找到了这个解决方案:

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

#18


3  

As with @Kametrixom answer here I believe returning an array would be better than returning AnySequence, since you can have access to all of Array's goodies such as count, etc.

与@Kametrixom的回答一样,我认为返回一个数组比返回任何序列都要好,因为您可以访问所有数组的优点,比如count等。

Here's the re-write:

重写:

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

#19


2  

Enums have toRaw() and fromRaw() methods so if your raw value is an Int, you can iterate from the first to last enum:

Enums有toRaw()和fromRaw()方法,因此如果您的原始值是Int,您可以从第一次到最后一次枚举迭代:

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

One gotcha is that you need to test for optional values before running the simpleDescription method, so we set convertedSuit to our value first and then set a constant to convertedSuit.simpleDescription()

一个问题是,在运行simpleDescription方法之前,您需要对可选值进行测试,因此我们首先将convertedSuit设置为我们的值,然后将一个常量设置为convertedSuit.simpleDescription()

#20


2  

This seems like a hack but if you use raw values you can do something like this

这看起来像一个hack,但是如果你使用原始值,你可以做类似的事情。

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

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

#21


2  

Here's my suggested approach. It's not completely satisfactory (I'm very new to Swift and OOP!) but maybe someone can refine it. The idea is to have each enum provide its own range information as .first and .last properties. It adds just two lines of code to each enum: still a bit hard-coded, but at least it's not duplicating the whole set. It does require modifying the Suit enum to be an Int like the Rank enum is, instead of untyped.

这是我的建议的方法。这并不完全令人满意(我对Swift和OOP非常熟悉),但也许有人可以改进它。这个想法是让每个enum提供它自己的范围信息。它只向每个enum添加了两行代码:仍然有一些硬编码,但至少它没有复制整个集合。它确实需要修改Suit enum,使它像Rank enum一样,而不是没有类型的。

Rather than echo the whole solution, here's the code I added to the Rank enum, somewhere after the case statements (Suit enum is similar):

与其呼应整个解决方案,下面是我添加到Rank enum的代码,在case语句之后的某个地方(Suit enum类似):

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

and the loop I used to build the deck as an array of String. (The problem definition did not state how the deck was to be structured.)

而我用来构建deck的循环是字符串数组。(问题的定义并没有说明该平台的结构。)

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
}

It's unsatisfactory because the properties are associated to an element rather than to the enum. But it does add clarity to the 'for' loops. I'd like it to say Rank.first instead of Rank.Ace.first. It works (with any element), but it's ugly. Can someone show how to elevate that to the enum level?

这并不令人满意,因为属性与元素相关联,而不是与enum相关。但它确实为“for”循环增加了清晰度。我希望它是Rank。第一,而不是Rank.Ace.first。它可以工作(任何元素),但它很丑。有人能展示如何将其提升到enum级别吗?

And to make it work, I lifted the createDeck method out of the Card struct... could not figure out how to get a [String] array returned from that struct, and that seems a bad place to put such a method anyway.

为了使它有效,我从卡片结构中取出createDeck方法…不知道如何从该结构中返回一个[String]数组,这似乎是放置这种方法的一个坏地方。

#22


2  

I did it using computed property, which returns the array of all values (thanks to this post http://natecook.com/blog/2014/10/loopy-random-enum-ideas/). However it also uses int raw-values, but I don't need to repeat all members of enumeration in separate property.

我使用计算属性来完成它,它返回所有值的数组(感谢这篇文章http://natecook.com/blog/2014/10/loopy-random-ideas/)。但是,它也使用int raw-values,但是我不需要在单独的属性中重复所有枚举成员。

UPDATE Xcode 6.1 changed a bit a way how to get enum member using raw value, so I fixed listing. Also fixed small error with wrong first raw value

更新Xcode 6.1更改了如何使用原始值获取enum成员的方式,所以我将其固定在了清单上。还用错误的第一个原始值修正小错误。

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

#23


2  

While dealing with Swift 2.0 here is my suggestion:

在处理Swift 2.0的同时,我的建议是:

I have added the raw type to Suit enum

我已经将原始类型添加到Suit enum。

enum Suit: Int {

then:

然后:

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

#24


2  

There are lots of good solutions in this thread and others however some of them are very complicated. I like to simplify as much as possible. Here is a solution which may or may not work for different needs but I think it works well in most cases:

在这个线程中有很多很好的解决方案,但是其中一些很复杂。我喜欢尽可能地简化。这里有一个解决方案,它可能适用于不同的需求,但我认为它在大多数情况下都适用:

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

To iterate:

迭代:

for item in Number.allValues {
    print("number is: \(item)")
}

#25


2  

For enum representing 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 }
}

Call it like this:

叫它是这样的:

print(Filter.allValues)

Prints:

打印:

[0, 1, 2, 3, 4]

[0,1,2,3,4]


For enum representing 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"
        }
    }
}

Call it

叫它

print(Filter.allValues)

Prints:

打印:

["Salary", "Experience", "Technology", "Unutilized", "Unutilized High Value"]

[“工资”、“经验”、“技术”、“未利用”、“未利用的高价值”]

#26


2  

Another solution:

另一个解决方案:

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

#27


2  

You can try to enumerate like this

你可以试着这样列举。

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]

#28


2  

Here a method I use to both iterate an enum, and provide multiple values types from one 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"))")

This is the output:

这是输出:

The number Zero in french: zéro, spanish: cero, japanese: nuru
The number One in french: un, spanish: uno, japanese: ichi
The number Two in french: deux, spanish: dos, japanese: ni
The number Three in french: trois, spanish: tres, japanese: san
The number Four in french: quatre, spanish: cuatro, japanese: shi
The number Five in french: cinq, spanish: cinco, japanese: go
The number Six in french: six, spanish: seis, japanese: roku
The number Seven in french: sept, spanish: siete, japanese: shichi

法国零数量:0,西班牙:大马鲛,日本:nuru数量一个在法国:联合国,西班牙:uno,日本:阿2号在法国:两个,西班牙:dos,日本:倪法国的3号:三,西班牙:非常,日本:圣法国的4号:四点,西班牙:四弦吉他,日本:“法国的5号:五点,西班牙:五、日本:去法国的6号:6、西班牙:检波器,日本:roku法国7数量:9月,西班牙:siete,日本:shichi

Total of 8 cases

共有8例

siete in japanese: shichi

日本:siete shichi

#29


1  

I have used the below method, the assumption is that I know which is the last value in the Rank enum and all the ranks have incremental values after Ace

我使用了下面的方法,假设是我知道哪个是Rank enum的最后一个值,并且所有的秩都有递增的值。

I prefer this way as it is clean and small, easy to understand

我喜欢这种方式,因为它干净、小巧,容易理解。

 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
}

#30


1  

There is a clever way, and frustrating as it is it illustrates the difference between the two different kinds of enums.

有一种聪明的方法,令人沮丧的是它说明了两种不同的枚举之间的区别。

Try this:

试试这个:

    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
    }

The deal is that an enum backed by numbers (raw values) is implicitly explicitly ordered, whereas an enum that isn't backed by numbers is explicitly implicitly unordered.

该协议是一个由数字(原始值)支持的枚举,它是隐式显式有序的,而没有数字支持的枚举是显式隐式无序的。

E.g. when we give the enum values numbers, the language is cunning enough to figure out what order the numbers are in. If on the other hand we don't give it any ordering, when we try to iterate over the values the language throws its hands up in the air and goes "yes, but which one do you want to go first???"

当我们给枚举值编号时,语言足够狡猾,可以找出数字的顺序。另一方面,如果我们不给它任何排序,当我们试图迭代这些值时,语言会把它的手举到空中,然后说,“是的,但是你想先去哪一个呢?”

Other languages which can do this (iterating over unordered enums) might be the same languages where everything is 'under the hood' actually a map or dictionary, and you can iterate over the keys of a map, whether there's any logical ordering or not.

其他可以这样做的语言(遍历无序的枚举)可能是相同的语言,所有的东西都是“底层”的,实际上是一个映射或字典,您可以遍历映射的键,不管是否有逻辑排序。

So the trick is to provide it with something that is explicitly ordered, in this case instances of the suits in an array in the order we want. As soon as you give it that, Swift is like "well why didn't you say so in the first place?"

关键是要提供一个明确有序的东西,在这个例子中,在数组中的实例中,我们想要的顺序。一旦你给出了答案,斯威夫特就会说:“你为什么一开始就不这么说呢?”

The other shorthand trick is to use the forcing operator on the fromRaw function. This illustrates another 'gotcha' about enums, that the range of possible values to pass in is often larger than the range of enums. For instance if we said Rank.fromRaw(60) there wouldn't be a value returned, so we're using the optional feature of the language, and where we start using optionals, forcing will soon follow. (Or alternately the if let construction which still seems a bit weird to me)

另一种简写技巧是在fromRaw函数上使用强制操作符。这说明了关于枚举的另一个“问题”,即传入的可能值的范围通常大于枚举的范围。例如,如果我们说Rank.fromRaw(60),就不会返回值,因此我们将使用该语言的可选特性,然后我们开始使用optionals,强制将很快跟随。(或者,如果让我觉得有点奇怪的话,让我来做吧。)