【问题标题】:How to get all enum values as an array如何将所有枚举值作为数组获取
【发布时间】:2016-01-02 07:47:53
【问题描述】:

我有以下枚举。

enum EstimateItemStatus: Printable {
    case Pending
    case OnHold
    case Done

    var description: String {
        switch self {
        case .Pending: return "Pending"
        case .OnHold: return "On Hold"
        case .Done: return "Done"
        }
    }

    init?(id : Int) {
        switch id {
        case 1:
            self = .Pending
        case 2:
            self = .OnHold
        case 3:
            self = .Done
        default:
            return nil
        }
    }
}

我需要将所有原始值作为字符串数组获取(例如["Pending", "On Hold", "Done"])。

我将此方法添加到枚举中。

func toArray() -> [String] {
    var n = 1
    return Array(
        GeneratorOf<EstimateItemStatus> {
            return EstimateItemStatus(id: n++)!.description
        }
    )
}

但我收到以下错误。

找不到接受类型为“(() -> _)”的参数列表的“GeneratorOf”类型的初始化程序

有没有更简单、更好或更优雅的方法来做到这一点?

【问题讨论】:

  • 你可以像 let array 一样创建数组:[EstimateItemStatus] = [.Pending, .Onhold, .Done]
  • @KristijanDelivuk 我想将此功能添加到枚举本身。因此,如果我向枚举添加另一个值,我不必在代码库的其他地方到处添加它。
  • 我有一个答案你可以参考这里stackoverflow.com/a/48960126/5372480

标签: ios arrays swift enums


【解决方案1】:

适用于 Swift 4.2 (Xcode 10) 及更高版本

有一个CaseIterable 协议:

enum EstimateItemStatus: String, CaseIterable {
    case pending = "Pending"
    case onHold = "OnHold"
    case done = "Done"

    init?(id : Int) {
        switch id {
        case 1: self = .pending
        case 2: self = .onHold
        case 3: self = .done
        default: return nil
        }
    }
}

for value in EstimateItemStatus.allCases {
    print(value)
}

对于斯威夫特

不,您不能查询 enum 中包含的值。见this article。您必须定义一个列出您拥有的所有值的数组。另请查看“How to get all enum values as an array”中 Frank Valbuena 的解决方案。

enum EstimateItemStatus: String {
    case Pending = "Pending"
    case OnHold = "OnHold"
    case Done = "Done"

    static let allValues = [Pending, OnHold, Done]

    init?(id : Int) {
        switch id {
        case 1:
            self = .Pending
        case 2:
            self = .OnHold
        case 3:
            self = .Done
        default:
            return nil
        }
    }
}

for value in EstimateItemStatus.allValues {
    print(value)
}

【讨论】:

  • 查看这个答案:stackoverflow.com/a/28341290/8047 包括 Swift 3 代码。
  • 赞成 allValues 部分,但不确定枚举是 String 类型但使用 Int 初始化的感觉。
  • 第一个链接已损坏,但现在似乎位于exceptionshub.com/…
  • 如果我想通过检查条件来删除“OnHold”,最好的方法是什么?
【解决方案2】:

斯威夫特 5

CaseIterable 协议添加到枚举中:

enum EstimateItemStatus: String, CaseIterable {
    case pending = "Pending"
    case onHold = "OnHold"
    case done = "Done"
}

用法:

let values: [String] = EstimateItemStatus.allCases.map { $0.rawValue }
//["Pending", "OnHold", "Done"]

【讨论】:

    【解决方案3】:

    Swift 4.2引入了一个名为CaseIterable的新协议

    enum Fruit : CaseIterable {
        case apple , apricot , orange, lemon
    }
    

    当你符合时,你可以从enum这样的情况下得到一个数组

    for fruit in Fruit.allCases {
        print("I like eating \(fruit).")
    }
    

    【讨论】:

      【解决方案4】:
      enum EstimateItemStatus: String, CaseIterable {
        case pending = "Pending"
        case onHold = "OnHold"
        case done = "Done"
      
        static var statusList: [String] {
          return EstimateItemStatus.allCases.map { $0.rawValue }
        }
      }
      

      [“待处理”、“等待”、“完成”]

      【讨论】:

      • 有了简写,这看起来很酷..
      • 这就是我想要的!感谢您提供的一行代码
      【解决方案5】:

      还有另一种方式,至少在编译时是安全的:

      enum MyEnum {
          case case1
          case case2
          case case3
      }
      
      extension MyEnum {
          static var allValues: [MyEnum] {
              var allValues: [MyEnum] = []
              switch (MyEnum.case1) {
              case .case1: allValues.append(.case1); fallthrough
              case .case2: allValues.append(.case2); fallthrough
              case .case3: allValues.append(.case3)
              }
              return allValues
          }
      }
      

      请注意,这适用于任何枚举类型(RawRepresentable 与否),并且如果您添加一个新案例,那么您将收到一个编译器错误,这很好,因为这将迫使您更新它。

      【讨论】:

      • 非正统,但它可以工作,并且如果您修改枚举案例,它会警告您。聪明的解决方案!
      【解决方案6】:

      要获得功能性列表,请使用表达式EnumName.allCases,它返回一个数组,例如

      EnumName.allCases.map{$0.rawValue} 
      

      会给你一个字符串列表给你EnumName: String, CaseIterable

      注意:使用allCases 而不是AllCases()

      【讨论】:

        【解决方案7】:

        我在某处找到了这段代码:

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

        用途:

        enum YourEnum: EnumCollection { //code }
        
        YourEnum.cases()
        

        从 YourEnum 返回案例列表

        【讨论】:

        • 似乎是一个很好的解决方案,但在 Swift 4 上有不少编译错误。
        • “某处”可能是:theswiftdev.com/2017/10/12/swift-enum-all-values(等等?)。博主感谢CoreKit
        • 这在 XCode 10(不管 Swift 版本)中中断,因为枚举的 hashValue 不再是增量的而是随机的,从而破坏了机制。这样做的新方法是升级到 Swift 4.2 并使用 CaseIterable
        【解决方案8】:

        Swift 5 更新

        我发现的最简单的解决方案是在扩展 CaseIterable 的枚举上使用 .allCases

        enum EstimateItemStatus: CaseIterable {
            case Pending
            case OnHold
            case Done
        
            var description: String {
                switch self {
                case .Pending: return "Pending"
                case .OnHold: return "On Hold"
                case .Done: return "Done"
                }
            }
        
            init?(id : Int) {
                switch id {
                case 1:
                    self = .Pending
                case 2:
                    self = .OnHold
                case 3:
                    self = .Done
                default:
                    return nil
                }
            }
        }
        

        任何CaseIterable 枚举上的.allCases 都将返回该元素的Collection

        var myEnumArray = EstimateItemStatus.allCases
        

        更多关于CaseIterable的信息

        【讨论】:

        • 不需要实现description()。只需将每个 case 等同于字符串,例如 case OnHold = "On Hold",这将成为每个 case 的原始值。
        • @pnizzle 我知道,因为它在原始问题中。
        【解决方案9】:

        你可以使用

        enum Status: Int{
            case a
            case b
            case c
        
        }
        
        extension RawRepresentable where Self.RawValue == Int {
        
            static var values: [Self] {
                var values: [Self] = []
                var index = 1
                while let element = self.init(rawValue: index) {
                    values.append(element)
                    index += 1
                }
                return values
            }
        }
        
        
        Status.values.forEach { (st) in
            print(st)
        }
        

        【讨论】:

        • 不错!从 Swift 3.2 升级到 4.1 后,这是我使用的解决方案。我们最初有 AnyItertor 声明。您的解决方案更清晰,更易于阅读。谢谢!
        • 这里有一个代码缺陷。它错过了案例中的第一项。将 var index = 1 更改为 var index = 0
        【解决方案10】:

        对于 Swift 2

        // Found http://stackoverflow.com/questions/24007461/how-to-enumerate-an-enum-with-string-type
        func iterateEnum<T where T: Hashable, T: RawRepresentable>(_: 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
                }
            }
        }
        
        func arrayEnum<T where T: Hashable, T: RawRepresentable>(type: T.Type) -> [T]{
            return Array(iterateEnum(type))
        }
        

        使用它:

        arrayEnum(MyEnumClass.self)
        

        【讨论】:

        • 为什么元素的hashValue 会是0..n
        【解决方案11】:

        Sequence 的启发和数小时的尝试 n 错误之后。我终于在 Xcode 9.1 上得到了这个舒适漂亮的 Swift 4 方式:

        protocol EnumSequenceElement: Strideable {
            var rawValue: Int { get }
            init?(rawValue: Int)
        }
        
        extension EnumSequenceElement {
            func distance(to other: Self) -> Int {
                return other.rawValue - rawValue
            }
        
            func advanced(by n: Int) -> Self {
                return Self(rawValue: n + rawValue) ?? self
            }
        }
        
        struct EnumSequence<T: EnumSequenceElement>: Sequence, IteratorProtocol {
            typealias Element = T
        
            var current: Element? = T.init(rawValue: 0)
        
            mutating func next() -> Element? {
                defer {
                    if let current = current {
                        self.current = T.init(rawValue: current.rawValue + 1)
                    }
                }
                return current
            }
        }
        

        用法:

        enum EstimateItemStatus: Int, EnumSequenceElement, CustomStringConvertible {
            case Pending
            case OnHold
            case Done
        
            var description: String {
                switch self {
                case .Pending:
                    return "Pending"
                case .OnHold:
                    return "On Hold"
                case .Done:
                    return "Done"
                }
            }
        }
        
        for status in EnumSequence<EstimateItemStatus>() {
            print(status)
        }
        // Or by countable range iteration
        for status: EstimateItemStatus in .Pending ... .Done {
            print(status)
        }
        

        输出:

        Pending
        On Hold
        Done
        

        【讨论】:

          【解决方案12】:

          如果您的枚举是增量的并且与数字相关联,您可以使用映射到枚举值的数字范围,如下所示:

          // Swift 3
          enum EstimateItemStatus: Int {
              case pending = 1,
              onHold
              done
          }
          
          let estimateItemStatusValues: [EstimateItemStatus?] = (EstimateItemStatus.pending.rawValue...EstimateItemStatus.done.rawValue).map { EstimateItemStatus(rawValue: $0) }
          

          这不适用于与字符串或除数字以外的任何内容相关联的枚举,但如果是这种情况,它会很好用!

          【讨论】:

            【解决方案13】:

            对枚举进行扩展以创建 allValues。

            extension RawRepresentable where Self: CaseIterable {
                  static var allValues: [Self.RawValue] {
                    return self.allCases.map { $0.rawValue}
                  }
                }
            

            【讨论】:

            • 虽然此代码可能会为 OP 的问题提供解决方案,但强烈建议您提供有关此代码为何和/或如何回答问题的额外上下文。从长远来看,只有代码的答案通常会变得毫无用处,因为未来遇到类似问题的观众无法理解解决方案背后的原因。
            猜你喜欢
            • 1970-01-01
            • 2011-04-18
            • 1970-01-01
            • 2011-01-17
            • 2020-01-12
            • 2014-09-24
            • 2015-06-12
            • 2021-12-15
            • 1970-01-01
            相关资源
            最近更新 更多