如何枚举String类型的枚举?

时间:2014-06-03 05:03:13

标签: swift string enums enumerate

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

例如,我该怎么做:

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

结果示例:

♠
♥
♦
♣

41 个答案:

答案 0 :(得分:506)

这篇文章与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
}

答案 1 :(得分:272)

我创建了一个实用程序函数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的内存表示 - 排除带有关联类型的enum - 只是案例的索引,当案例的计数为2...256时,它与{{1}相同},UInt8时,257...65536等等。因此,它可以是UInt16来自相应的无符号整数类型。
  • 枚解值的
  • unsafeBitcast与案例的索引相同。
  • 来自无效索引的
  • .hashValue枚举值为.hashValue

<强>增加:

针对Swift2进行了修订,并实施了来自@Kametrixom's answer

的投射创意
0

<强>增加: 针对Swift3进行了修订

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.0.1

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

答案 2 :(得分:200)

Swift 4.2 +

Swift 4.2开始(使用Xcode 10),只需将协议一致性添加到CaseIterable即可从allCases中受益:

extension Suit: CaseIterable {}

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

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

与早期Swift版本(3.x和4.x)的兼容性

模仿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

答案 3 :(得分:127)

其他解决方案工作但是他们都假设例如可能的等级和诉讼的数量,或者第一和最后等级可能是什么。诚然,在可预见的未来,一副牌的布局可能不会有太大变化。但是,一般来说,编写尽可能少的假设的代码更为简洁。我的解决方案:

我在套装枚举中添加了原始类型,因此我可以使用Suit(rawValue :)来访问套装案例:

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

下面是Card的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()

答案 4 :(得分:73)

真正有效的第二个答案

所以我在位和字节中偶然发现并创建了一个扩展(我后来发现它与@rintaro的答案非常相似)。它可以这样使用:

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

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

值得注意的是它可以在任何枚举上使用(没有相关值)。请注意,这不适用于没有案例的枚举。

声明

@rintaro的答案一样,此代码使用枚举的基础表示。这种表示没有记录,将来可能会改变,这会破坏它 - &gt;我不建议在生产中使用它。

代码(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,但编译器抱怨没有它)

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

答案 5 :(得分:25)

您可以通过实施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
}

答案 6 :(得分:17)

原则上可以这样做,假设您没有为枚举的情况使用原始值赋值:

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 :(得分:14)

此问题现在变得更加容易。这是我的Swift 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
}

Pre 4.2

我喜欢这个解决方案,我找到这个页面后放在一起: List comprehension in Swift

它使用Int raws而不是Strings,但它避免键入两次,它允许自定义范围,并且不会硬编码原始值。

这是我原始解决方案的Swift 4版本,但请参阅上面的4.2改进。

{{1}}

答案 8 :(得分:13)

如果你给出枚举a raw Int value,它将使循环变得更容易。

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

最终的Suit类,包括用more standard CustomStringConvertible protocol替换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)
}

修改

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

答案 9 :(得分: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 an swer

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

答案 10 :(得分: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"]

答案 11 :(得分:6)

编辑: Swift Evolution Proposal SE-0194 Derived Collection of Enum Cases提出了针对此问题的解决方案级别。我们在Swift 4.2和更新版本中看到它。该提案还指出一些workarounds与此处已经提及的一些类似,但不过可能会看到它。

为了完整起见,我还会保留原帖。

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

答案 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)

对不起,我的回答是针对我如何在我需要做的事情中使用这篇文章。对于那些偶然发现这个问题的人,寻找一种方法来在枚举中找到一个案例,这就是这样做的方法(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)
        }
    }

对于那些想知道enum枚举的人来说,这个页面上给出的包含一个包含所有枚举值数组的静态var / let的答案都是正确的。 tvOS的最新Apple示例代码包含了这种完全相同的技术。

话虽这么说,他们应该在语言中建立一个更方便的机制(Apple,你在听吗?)!

答案 14 :(得分: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]

答案 15 :(得分: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()

答案 16 :(得分:4)

Xcode 10 with 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)

打印:

  

[“薪水”,“经验”,“技术”,“未使用”,“未使用的高价值”]

答案 17 :(得分:4)

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

答案 18 :(得分: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()

答案 19 :(得分:2)

这是一篇非常古老的文章,来自Swift 2.0。现在有一些更好的解决方案,使用swift 3.0的新功能: Iterating through an Enum in Swift 3.0

在这个问题上,有一个解决方案使用了一个新功能(在我编写此编辑时尚未发布)Swift 4.2: How do I get the count of a Swift enum?

这个帖子中有很多好的解决方案,但其中一些很复杂。我想尽可能简化。这是一个解决方案,可能会或可能不会满足不同的需求,但我认为它在大多数情况下运作良好:

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

答案 20 :(得分:2)

与@Kametrixom一样回答here我相信返回一个数组会比返回AnySequence更好,因为你可以访问所有Array的好东西,比如count等。

这是重写:

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

答案 21 :(得分:2)

枚举有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()
    }
}

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

答案 22 :(得分: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)
}

答案 23 :(得分:2)

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

UPDATE Xcode 6.1改变了一些如何使用原始值获取枚举成员的方法,所以我修复了列表。还修复了错误的第一个原始值的小错误

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

答案 24 :(得分:2)

这是我的建议方法。它并不完全令人满意(我对Swift和OOP来说很新!)但也许有人可以改进它。我们的想法是让每个枚举提供自己的范围信息,如.first和.last属性。它只为每个枚举添加两行代码:仍然有点硬编码,但至少它不会复制整个集合。它确实需要将Suit枚举修改为类似于Rank枚举的Int,而不是无类型。

不是回应整个解决方案,而是我在Rank enum中添加的代码,在case语句之后的某个地方(Suit枚举类似):

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
}

它不能令人满意,因为属性与元素而不是枚举相关联。但它确实增加了“#”的清晰度。循环。我想说Rank.first而不是Rank.Ace.first。它适用(使用任何元素),但它很难看。有人可以展示如何将其提升到枚举级别吗?

为了使它工作,我将createDeck方法从Card结构中取出...无法弄清楚如何从该结构返回一个[String]数组,这似乎是放置这种方法的一个不好的地方反正。

答案 25 :(得分:2)

这似乎是一个黑客,但如果你使用原始值,你可以做这样的事情

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

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

答案 26 :(得分: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
    }
}

答案 27 :(得分:1)

我使用了下面的方法,假设我知道哪个是Rank枚举中的最后一个值,所有等级在Ace之后都有增量值

我更喜欢这种方式,因为它干净小巧,易于理解

 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
}

答案 28 :(得分:1)

更新后的代码: Swift 4.2 / Swift 5

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

要按问题访问输出:

for suitKey in Suit.allCases {
    print(suitKey.rawValue)
}

输出:

♠
♥
♦
♣

CaseIterable:提供了其所有值的集合。 符合CaseIterable协议的类型通常是没有关联值的枚举。使用CaseIterable类型时,您可以使用类型的allCases属性访问该类型所有案例的集合。

对于访问案例,我们使用 .allCases 。有关更多信息,请单击https://developer.apple.com/documentation/swift/caseiterable

答案 29 :(得分:1)

Swift 5解决方案: Swift 5中的解决方案非常简单:

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

// then access the cases like this:

for suitKey in LocalizationKey.allCases {
    print(suitKey)
}

答案 30 :(得分:1)

(对Karthik Ku​​mar的改进回答)

此解决方案使用编译器来保证您不会错过案例。

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

    static var enumerate: [Suit] {
        switch Suit.spades {
        // make sure the two lines are identical ^_^
        case        .spades, .hearts, .diamonds, .clubs:
            return [.spades, .hearts, .diamonds, .clubs]
        }
    }
}

答案 31 :(得分:1)

有一种聪明的方式,令人沮丧,因为它说明了两种不同类型的枚举之间的区别。

试试这个:

    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
    }

这笔交易是由数字(原始值)支持的枚举被隐式显式排序,而没有数字支持的枚举显式隐式无序。

E.g。当我们给出枚举值数字时,语言足够狡猾以确定数字的顺序。 另一方面,如果我们没有给它任何排序,当我们尝试迭代这些值时,语言会在空中举起并且“是的,但是你要先去哪一个? ?“

其他可以做到这一点的语言(迭代无序的枚举)可能是相同的语言,其中一切都是“幕后”实际上是一个地图或字典,你可以迭代地图的键,是否有任何逻辑排序或不。

所以诀窍是为它提供明确排序的东西,在这种情况下,按照我们想要的顺序排列数组中的西装实例。一旦你给它,Swift就像“那么你为什么不首先这么说呢?”

另一个简写技巧是在fromRaw函数上使用强制运算符。这说明了关于枚举的另一个“问题”,即传入的可能值的范围通常大于枚举的范围。例如,如果我们说Rank.fromRaw(60)没有返回值,那么我们使用该语言的可选功能,并且我们开始使用选项,强制将很快跟随。 (或者如果让构造对我来说似乎有些奇怪的话)

答案 32 :(得分: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
    }
}

根据Rick回答:这快5倍

答案 33 :(得分:0)

它花了我一点,然后只是一个方法在结构中,如所谓的swift书,但我在枚举中设置了下一个函数。我会使用一个协议,我不知道为什么,但将等级设置为int混乱

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.toRaw())
        }
    }
    mutating func next() -> Rank {
        var rank = self
        var rawrank = rank.toRaw()
        var nrank:Rank = self
        rawrank = rawrank + 1
        if let newRank = Rank.fromRaw(rawrank) {
            println("\(newRank.simpleDescription())")
            nrank = newRank
        } else {
            return self
        }
        return nrank
    }
}

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

struct Card {
    var rank:Rank
    var suit:Suit
    func deck() -> Card[] {
        var tRank = self.rank
        var tSuit = self.suit
        let tcards = 52 // we start from 0
        var cards: Card[] = []
        for i in 0..tcards{
            var card = Card(rank: tRank, suit: tSuit)
            cards.append(card)
            tRank = tRank.next()
            tSuit = tSuit.next()
        }
        return cards
    }
    func simpleDescription() -> String {
        return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
    }
}

var card = Card(rank: .Ace, suit: .Spades)
var deck = card.deck()

希望这有点帮助我基本上使用了一些常识,但可以通过按等级乘以西装来轻松纠正(如果你没有使用标准的牌组,你必须相应地更改枚举,如果基本上只是逐步通过不同的枚举注释以节省时间我使用排名原始值你可以做同样的西装如果你想但是例子没有它所以我决定弄清楚而不改变套装原始值

答案 34 :(得分:0)

我发现了一种有些hacky-feel但更安全的方法,它不需要输入两次值或引用枚举值的内存,因此不太可能破坏。

基本上,不是使用枚举,而是使用单个实例创建一个结构,并使所有枚举值常量。然后可以使用Mirror

查询变量
public struct Suit{

    // the values
    let spades = "♠"
    let hearts = "♥"
    let diamonds = "♦"
    let clubs = "♣"

    // make a single instance of the Suit struct, Suit.instance
    struct SStruct{static var instance: Suit = Suit()}
    static var instance : Suit{
        get{return SStruct.instance}
        set{SStruct.instance = newValue}
    }

    // an array with all of the raw values
    static var allValues: [String]{
        var values = [String]()

        let mirror = Mirror(reflecting: Suit.instance)
        for (_, v) in mirror.children{
            guard let suit = v as? String else{continue}
            values.append(suit)
        }

        return values
    }
}

如果您使用此方法,要获得单个值,您需要使用Suit.instance.clubsSuit.instance.spades

但所有这些都是如此无聊......让我们做一些让它更像真正的枚举的东西!

public struct SuitType{

    // store multiple things for each suit
    let spades = Suit("♠", order: 4)
    let hearts = Suit("♥", order: 3)
    let diamonds = Suit("♦", order: 2)
    let clubs = Suit("♣", order: 1)

    struct SStruct{static var instance: SuitType = SuitType()}
    static var instance : SuitType{
        get{return SStruct.instance}
        set{SStruct.instance = newValue}
    }

    // a dictionary mapping the raw values to the values
    static var allValuesDictionary: [String : Suit]{
        var values = [String : Suit]()

        let mirror = Mirror(reflecting: SuitType.instance)
        for (_, v) in mirror.children{
            guard let suit = v as? Suit else{continue}
            values[suit.rawValue] = suit
        }

        return values
    }
}

public struct Suit: RawRepresentable, Hashable{
    public var rawValue: String
    public typealias RawValue = String

    public var hashValue: Int{
        // find some integer that can be used to uniquely identify
        // each value. In this case, we could have used the order
        // variable because it is a unique value, yet to make this
        // apply to more cases, the hash table address of rawValue
        // will be returned, which should work in almost all cases
        // 
        // you could also add a hashValue parameter to init() and
        // give each suit a different hash value
        return rawValue.hash
    }

    public var order: Int
    public init(_ value: String, order: Int){
        self.rawValue = value
        self.order = order
    }

    // an array of all of the Suit values
    static var allValues: [Suit]{
        var values = [Suit]()

        let mirror = Mirror(reflecting: SuitType.instance)
        for (_, v) in mirror.children{
            guard let suit = v as? Suit else{continue}
            values.append(suit)
        }

        return values
    }

    // allows for using Suit(rawValue: "♦"), like a normal enum
    public init?(rawValue: String){
        // get the Suit from allValuesDictionary in SuitType, or return nil if that raw value doesn't exist
        guard let suit = SuitType.allValuesDictionary[rawValue] else{return nil}
        // initialize a new Suit with the same properties as that with the same raw value
        self.init(suit.rawValue, order: suit.order)
    }
}

你现在可以做像

这样的事情了
let allSuits: [Suit] = Suit.allValues

for suit in Suit.allValues{
   print("The suit \(suit.rawValue) has the order \(suit.order)")
}

但是,要获得单曲,您仍需要使用SuitType.instance.spadesSuitType.instance.hearts。为了使这更加直观,您可以向Suit添加一些代码,以便您使用Suit.type.*代替SuitType.instance.*

public struct Suit: RawRepresentable, Hashable{
   // ...your code...

   static var type = SuitType.instance

   // ...more of your code...
}

您现在可以使用Suit.type.diamonds代替SuitType.instance.diamonds,或Suit.type.clubs代替SuitType.instance.clubs

答案 35 :(得分:0)

有时,您可能会处理枚举类型,其基础原始整数类型会在整个软件开发生命周期中发生变化。这是一个适用于该情况的示例:

public class MyClassThatLoadsTexturesEtc
{
    //...

    // Colors used for gems and sectors.
    public enum Color: Int
    {
        // Colors arranged in order of the spectrum.
        case First = 0
        case Red, Orange, Yellow, Green, Blue, Purple, Pink
        // --> Add more colors here, between the first and last markers.
        case Last
    }

    //...

    public func preloadGems()
    {
        // Preload all gems.
        for i in (Color.First.toRaw() + 1) ..< (Color.Last.toRaw())
        {
            let color = Color.fromRaw(i)!
            loadColoredTextures(forKey: color)
        }
    }

    //...
}

答案 36 :(得分:-1)

我添加了函数count(),并迭代值:

ClassLoader

}

答案 37 :(得分:-1)

我的解决方案是声明一个包含所有枚举可能性的数组,因此可以遍历所有这些。

//Function inside struct Card
static func generateFullDeck() -> [Card] {
    let allRanks = [Rank.Ace, Rank.Two, Rank.Three, Rank.Four, Rank.Five, Rank.Six, Rank.Seven, Rank.Eight, Rank.Nine, Rank.Ten, Rank.Jack, Rank.Queen, Rank.King]
    let allSuits = [Suit.Hearts, Suit.Diamonds, Suit.Clubs, Suit.Spades]
    var myFullDeck: [Card] = []

    for myRank in allRanks {
        for mySuit in allSuits {
            myFullDeck.append(Card(rank: myRank, suit: mySuit))
        }
    }
    return myFullDeck
}

//actual use:
let aFullDeck = Card.generateFullDeck()    //Generate the desired full deck

var allDesc: [String] = []
for aCard in aFullDeck {
    println(aCard.simpleDescription())    //You'll see all the results in playground
}

答案 38 :(得分:-2)

在Swift上,可以像enum一样访问EnumType.Case类型:

let tableView = UITableView(frame: self.view.bounds, style: UITableViewStyle.Plain)

大多数情况下,当您有几个选项时,您只会使用enum类型,并且确切地知道您将在每个类型上做些什么。

使用for-in类型时使用enum结构没有多大意义。

你可以这样做,例如:

func sumNumbers(numbers : Int...) -> Int {
    var sum = 0

    for number in numbers{
        sum += number
    }

    return sum
}

答案 39 :(得分:-3)

如果您仍想使用RankSuit的枚举,这是一个不太神秘的示例。如果要使用for-in loop迭代每个数组,只需将它们收集到数组中即可。

标准52张牌组的例子:

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

enum Suit: Int {
    case Diamonds = 1, Clubs, Hearts, Spades
    func name() -> String {
        switch self {
        case .Diamonds:
            return "diamonds"
        case .Clubs:
            return "clubs"
        case .Hearts:
            return "hearts"
        case .Spades:
            return "spades"
        default:
            return "NOT A VALID SUIT"
        }
    }
}

let Ranks = [
    Rank.Ace,
    Rank.Two,
    Rank.Three,
    Rank.Four,
    Rank.Five,
    Rank.Six,
    Rank.Seven,
    Rank.Eight,
    Rank.Nine,
    Rank.Ten,
    Rank.Jack,
    Rank.Queen,
    Rank.King
]

let Suits = [
    Suit.Diamonds,
    Suit.Clubs,
    Suit.Hearts,
    Suit.Spades
]


class Card {
    var rank: Rank
    var suit: Suit

    init(rank: Rank, suit: Suit) {
        self.rank = rank
        self.suit = suit
    }
}

class Deck {
    var cards = Card[]()

    init() {
        for rank in Ranks {
            for suit in Suits {
                cards.append(Card(rank: rank, suit: suit))
            }
        }
    }
}

var myDeck = Deck()
myDeck.cards.count  // => 52

答案 40 :(得分:-3)

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

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, .Clubs: return "black"
        case .Hearts, .Diamonds: return "red"
        }
    }
}

struct Card {
    var rank: Rank
    var suit: Suit
    func simpleDescription() -> String {
        return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
    }

    static func createPokers() -> Card[] {
        let ranks = Array(Rank.Ace.toRaw()...Rank.King.toRaw())
        let suits = Array(Suit.Spades.toRaw()...Suit.Clubs.toRaw())
        let cards = suits.reduce(Card[]()) { (tempCards, suit) in
            tempCards + ranks.map { rank in
                Card(rank: Rank.fromRaw(rank)!, suit: Suit.fromRaw(suit)!)
            }
        }
        return cards
    }
}