如何获得Swift枚举的计数?

时间:2014-11-23 21:59:31

标签: swift enums count

如何确定Swift枚举中的个案数?

(我希望避免使用manually enumerating through all the values,或使用旧的" enum_count trick"如果可能的话。)

25 个答案:

答案 0 :(得分:138)

我有a blog post详细说明了这一点,但只要您的枚举原始类型是整数,就可以这样添加计数:

enum Reindeer: Int {
    case Dasher, Dancer, Prancer, Vixen, Comet, Cupid, Donner, Blitzen
    case Rudolph

    static let count: Int = {
        var max: Int = 0
        while let _ = Reindeer(rawValue: max) { max += 1 }
        return max
    }()
}

答案 1 :(得分:100)

从Swift 4.2(Xcode 10)开始,您可以声明 符合CaseIterable协议,这适用于所有人 没有关联值的枚举:

enum Stuff: CaseIterable {
    case first
    case second
    case third
    case forth
}

现在只需使用

获取案例数量
print(Stuff.allCases.count) // 4

有关详细信息,请参阅

答案 2 :(得分:86)

Xcode 10更新

在枚举中采用CaseIterable协议,它提供静态allCases属性,其中包含所有枚举案例Collection。只需使用其count属性即可了解枚举的具体情况。

请参阅马丁的答案以获得一个例子(并提出他的答案而不是我的答案)


警告:以下方法似乎不再适用。

我不知道任何计算枚举案例数量的通用方法。然而,我注意到枚举案例的hashValue属性是增量的,从零开始,并且顺序由声明案例的顺序决定。因此,最后一个枚举的散列加一个对应于个案的数量。

例如使用此枚举:

enum Test {
    case ONE
    case TWO
    case THREE
    case FOUR

    static var count: Int { return Test.FOUR.hashValue + 1}
}

count返回4.

我不能说这是一个规则还是将来会改变,所以使用风险自负:)

答案 3 :(得分:69)

我定义了一个可重用的协议,它根据Nate Cook发布的方法自动执行案例计数。

protocol CaseCountable {
    static var caseCount: Int { get }
}

extension CaseCountable where Self: RawRepresentable, Self.RawValue == Int {
    internal static var caseCount: Int {
        var count = 0
        while let _ = Self(rawValue: count) {
            count += 1
        }
        return count
    }
}

然后我可以重用此协议,例如如下:

enum Planet : Int, CaseCountable {
    case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
}
//..
print(Planet.caseCount)

答案 4 :(得分:35)

创建静态allValues数组,如此answer

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

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

...

let count = ProductCategory.allValues.count

当您想要枚举值并且适用于所有枚举类型

时,这也很有用

答案 5 :(得分:15)

如果实现没有任何反对使用整数枚举的内容,您可以添加一个名为Count的额外成员值来表示枚举中的成员数 - 请参阅下面的示例:

enum TableViewSections : Int {
  case Watchlist
  case AddButton
  case Count
}

现在,您可以通过调用TableViewSections.Count.rawValue来获取枚举中的成员数量,对于上面的示例,它将返回2。

当您在switch语句中处理枚举时,请确保在遇到您不期望的Count成员时抛出断言失败:

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  let currentSection: TableViewSections = TableViewSections.init(rawValue:section)!
  switch(currentSection) {
  case .Watchlist:
    return watchlist.count
  case .AddButton:
    return 1
  case .Count:
    assert(false, "Invalid table view section!")
  }
}

答案 6 :(得分:14)

这种功能可以返回枚举的计数。

Swift 2

func enumCount<T: Hashable>(_: T.Type) -> Int {
    var i = 1
    while (withUnsafePointer(&i) { UnsafePointer<T>($0).memory }).hashValue != 0 {
        i += 1
    }
    return i
}

Swift 3

func enumCount<T: Hashable>(_: T.Type) -> Int {
   var i = 1
   while (withUnsafePointer(to: &i, {
      return $0.withMemoryRebound(to: T.self, capacity: 1, { return $0.pointee })
   }).hashValue != 0) {
      i += 1
   }
      return i
   }

答案 7 :(得分:9)

哦,大家好,单元测试怎么样?

func testEnumCountIsEqualToNumberOfItemsInEnum() {

    var max: Int = 0
    while let _ = Test(rawValue: max) { max += 1 }

    XCTAssert(max == Test.count)
}

这与Antonio的解决方案相结合:

enum Test {

    case one
    case two
    case three
    case four

    static var count: Int { return Test.four.hashValue + 1}
}
主代码中的

为您提供 O(1)以及如果有人添加枚举案例five则会导致测试失败并且不会更新count

的实施

答案 8 :(得分:9)

  

带索引的字符串枚举

enum eEventTabType : String {
    case Search     = "SEARCH"
    case Inbox      = "INBOX"
    case Accepted   = "ACCEPTED"
    case Saved      = "SAVED"
    case Declined   = "DECLINED"
    case Organized  = "ORGANIZED"

    static let allValues = [Search, Inbox, Accepted, Saved, Declined, Organized]
    var index : Int {
       return eEventTabType.allValues.indexOf(self)!
    }
}

计数:eEventTabType.allValues.count

索引:objeEventTabType.index

享受:)

答案 9 :(得分:7)

此函数依赖于2个未记录的当前(Swift 1.1)enum行为:

  • enum的内存布局只是case的索引。如果案件数量从2到256,则为UInt8
  • 如果enum是从无效的案例索引中进行了位,则其hashValue0

因此请自担风险:)

func enumCaseCount<T:Hashable>(t:T.Type) -> Int {
    switch sizeof(t) {
    case 0:
        return 1
    case 1:
        for i in 2..<256 {
            if unsafeBitCast(UInt8(i), t).hashValue == 0 {
                return i
            }
        }
        return 256
    case 2:
        for i in 257..<65536 {
            if unsafeBitCast(UInt16(i), t).hashValue == 0 {
                return i
            }
        }
        return 65536
    default:
        fatalError("too many")
    }
}

用法:

enum Foo:String {
    case C000 = "foo"
    case C001 = "bar"
    case C002 = "baz"
}
enumCaseCount(Foo) // -> 3

答案 10 :(得分:5)

我写了一个简单的扩展,它给出了原始值为整数count属性的所有枚举:

extension RawRepresentable where RawValue: IntegerType {
    static var count: Int {
        var i: RawValue = 0
        while let _ = Self(rawValue: i) {
            i = i.successor()
        }
        return Int(i.toIntMax())
    }
}

不幸的是,它将count属性提供给OptionSetType,但它无法正常工作,因此这是另一个需要明确符合CaseCountable协议的任何枚举的版本你想数数:

protocol CaseCountable: RawRepresentable {}
extension CaseCountable where RawValue: IntegerType {
    static var count: Int {
        var i: RawValue = 0
        while let _ = Self(rawValue: i) {
            i = i.successor()
        }
        return Int(i.toIntMax())
    }
}

它与Tom Pelaia发布的方法非常相似,但适用于所有整数类型。

答案 11 :(得分:4)

当然,它不是动态的,但对于许多用途,你可以通过添加到你的Enum的静态var来实现

static var count: Int{ return 7 }

然后将其用作EnumName.count

答案 12 :(得分:2)

对于我的用例,在一个代码库中,多个人可能正在为枚举添加密钥,并且这些情况应该都在allKeys属性中可用,重要的是allKeys要对枚举中的密钥进行验证。 这是为了避免有人忘记将其密钥添加到所有密钥列表。匹配allKeys数组的计数(首先创建为一组以避免欺骗)与枚举中的密钥数量相匹配,确保他们都在场。

上面的一些答案显示了在Swift 2中实现这一目标的方法,但在 Swift 3 中都没有。这是 Swift 3 格式化版本:

static func enumCount<T: Hashable>(_ t: T.Type) -> Int {
    var i = 1
    while (withUnsafePointer(to: &i) {
      $0.withMemoryRebound(to:t.self, capacity:1) { $0.pointee.hashValue != 0 }
    }) {
      i += 1
    }
    return i
}

static var allKeys: [YourEnumTypeHere] {
    var enumSize = enumCount(YourEnumTypeHere.self)

    let keys: Set<YourEnumTypeHere> = [.all, .your, .cases, .here]
    guard keys.count == enumSize else {
       fatalError("Missmatch between allKeys(\(keys.count)) and actual keys(\(enumSize)) in enum.")
    }
    return Array(keys)
}

根据您的使用情况,您可能希望在开发中运行测试以避免在每个请求上使用allKeys的开销

答案 13 :(得分:2)

为什么你这么复杂? Int enum的SIMPLEST计数器是添加:

case Count

最后。而且......中提琴 - 现在你有了计数 - 快速而简单

答案 14 :(得分:2)

enum EnumNameType: Int {
    case first
    case second
    case third

    static var count: Int { return EnumNameType.third.rawValue + 1 }
}

print(EnumNameType.count) //3

OR

enum EnumNameType: Int {
    case first
    case second
    case third
    case count
}

print(EnumNameType.count.rawValue) //3

*在Swift 4.2(Xcode 10)上可以使用:

enum EnumNameType: CaseIterable {
    case first
    case second
    case third
}

print(EnumNameType.allCases.count) //3

答案 15 :(得分:1)

使用Int类型枚举的 Swift 3 版本:

protocol CaseCountable: RawRepresentable {}
extension CaseCountable where RawValue == Int {
    static var count: RawValue {
        var i: RawValue = 0
        while let _ = Self(rawValue: i) { i += 1 }
        return i
    }
}

致谢:根据bzz和Nate Cook的回答。

不支持Generic IntegerType(在Swift 3中重命名为Integer),因为它是一个严重碎片化的泛型类型,缺少很多功能。

请注意,Code Commander对Nate Cooks的回答仍然有效:

  

虽然很好,因为你不需要对一个值进行硬编码,但这样做会   每次调用时都实例化每个枚举值。那是O(n)   而不是O(1)。

据我所知,当使用它作为协议扩展时没有解决方法(并且没有像Nate Cook那样在每个枚举中实现),因为泛型类型不支持静态存储属性。

无论如何,对于小枚举,这应该没问题。一个典型的用例是Zorayr已经提到的successor section.count

答案 16 :(得分:1)

扩展Matthieu Riegler的答案,这是 Swift 3 的解决方案,不需要使用泛型,并且可以使用EnumType.elementsCount的枚举类型轻松调用:

extension RawRepresentable where Self: Hashable {

    // Returns the number of elements in a RawRepresentable data structure
    static var elementsCount: Int {
        var i = 1
        while (withUnsafePointer(to: &i, {
            return $0.withMemoryRebound(to: self, capacity: 1, { return 
                   $0.pointee })
        }).hashValue != 0) {
            i += 1
        }
        return i
}

答案 17 :(得分:1)

如果您不想在最后一次枚举中使用代码,可以在枚举中创建此功能。

func getNumberOfItems() -> Int {
    var i:Int = 0
    var exit:Bool = false
    while !exit {
        if let menuIndex = MenuIndex(rawValue: i) {
            i++
        }else{
            exit = true
        }
    }
    return i
}

答案 18 :(得分:0)

我通过创建一个协议(EnumIntArray)和一个全局实用程序函数(enumIntArray)来解决这个问题,这使得添加一个&#34; All&#34;变量到任何枚举(使用swift 1.2)。 &#34;所有&#34;变量将包含枚举中所有元素的数组,因此您可以使用all.count作为计数

它仅适用于使用Int类型原始值的枚举,但也许它可以为其他类型提供一些灵感。

它还解决了编号中的差距&#34;并且&#34;过多的时间来迭代&#34;我在上面和其他地方读过的问题。

我们的想法是将EnumIntArray协议添加到您的枚举中,然后定义一个&#34; all&#34;静态变量通过调用enumIntArray函数并为其提供第一个元素(如果编号中存在间隙,则为最后一个元素)。

因为静态变量只初始化一次,所以浏览所有原始值的开销只会打到你的程序一次。

示例(无间隙):

enum Animals:Int, EnumIntArray
{ 
  case Cat=1, Dog, Rabbit, Chicken, Cow
  static var all = enumIntArray(Animals.Cat)
}

示例(有差距):

enum Animals:Int, EnumIntArray
{ 
  case Cat    = 1,  Dog, 
  case Rabbit = 10, Chicken, Cow
  static var all = enumIntArray(Animals.Cat, Animals.Cow)
}

以下是实现它的代码:

protocol EnumIntArray
{
   init?(rawValue:Int)
   var rawValue:Int { get }
}

func enumIntArray<T:EnumIntArray>(firstValue:T, _ lastValue:T? = nil) -> [T]
{
   var result:[T] = []
   var rawValue   = firstValue.rawValue
   while true
   { 
     if let enumValue = T(rawValue:rawValue++) 
     { result.append(enumValue) }
     else if lastValue == nil                     
     { break }

     if lastValue != nil
     && rawValue  >  lastValue!.rawValue          
     { break }
   } 
   return result   
}

答案 19 :(得分:0)

或者你可以在枚举之外定义_count,并静态附加它:

let _count: Int = {
    var max: Int = 0
    while let _ = EnumName(rawValue: max) { max += 1 }
    return max
}()

enum EnumName: Int {
    case val0 = 0
    case val1
    static let count = _count
}

这样,不管你创建了多少个枚举,它都只会被创建一次。

(如果static这样做,请删除此答案)

答案 20 :(得分:0)

以下方法来自CoreKit,与其他人提出的答案类似。这适用于Swift 4。

$.each($('select[multiple]'), function (key, selectbox) {
    $('#' + selectbox.id).multiselect({
        texts: {placeholder: $('#' + selectbox.id).attr('title')}
    });
});
public protocol EnumCollection: Hashable {
    static func cases() -> AnySequence<Self>
    static var allValues: [Self] { get }
}

public extension EnumCollection {

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

    public static var allValues: [Self] {
        return Array(self.cases())
    }
}

然后你只需要拨打enum Weekdays: String, EnumCollection { case sunday, monday, tuesday, wednesday, thursday, friday, saturday }

答案 21 :(得分:0)

enum WeekDays : String , CaseIterable
{

case monday = "Mon"
case tuesday = "Tue"
case wednesday = "Wed"
case thursday = "Thu"
case friday = "Fri"
case saturday = "Sat"
case sunday = "Sun"
}

var weekdays = WeekDays.AllCases()

print(“(weekdays.count)”)

答案 22 :(得分:-1)

struct HashableSequence<T: Hashable>: SequenceType {
    func generate() -> AnyGenerator<T> {
        var i = 0
        return AnyGenerator {
            let next = withUnsafePointer(&i) { UnsafePointer<T>($0).memory }
            if next.hashValue == i {
                i += 1
                return next
            }
            return nil
        }
    }
}

extension Hashable {
    static func enumCases() -> Array<Self> {
        return Array(HashableSequence())
    }

    static var enumCount: Int {
        return enumCases().enumCount
    }
}

enum E {
    case A
    case B
    case C
}

E.enumCases() // [A, B, C]
E.enumCount   //  3

但请注意非枚举类型的使用。一些解决方法可能是:

struct HashableSequence<T: Hashable>: SequenceType {
    func generate() -> AnyGenerator<T> {
        var i = 0
        return AnyGenerator {
            guard sizeof(T) == 1 else {
                return nil
            }
            let next = withUnsafePointer(&i) { UnsafePointer<T>($0).memory }
            if next.hashValue == i {
                i += 1
                return next
            }

            return nil
        }
    }
}

extension Hashable {
    static func enumCases() -> Array<Self> {
        return Array(HashableSequence())
    }

    static var enumCount: Int {
        return enumCases().count
    }
}

enum E {
    case A
    case B
    case C
}

Bool.enumCases()   // [false, true]
Bool.enumCount     // 2
String.enumCases() // []
String.enumCount   // 0
Int.enumCases()    // []
Int.enumCount      // 0
E.enumCases()      // [A, B, C]
E.enumCount        // 4

答案 23 :(得分:-1)

它可以使用一个静态常量,它包含枚举的最后一个值加一。

enum Color : Int {
    case  Red, Orange, Yellow, Green, Cyan, Blue, Purple

    static let count: Int = Color.Purple.rawValue + 1

    func toUIColor() -> UIColor{
        switch self {
            case .Red:
                return UIColor.redColor()
            case .Orange:
                return UIColor.orangeColor()
            case .Yellow:
                return UIColor.yellowColor()
            case .Green:
                return UIColor.greenColor()
            case .Cyan:
                return UIColor.cyanColor()
            case .Blue:
                return UIColor.blueColor()
            case .Purple:
                return UIColor.redColor()
        }
    }
}

答案 24 :(得分:-3)

这是次要的,但我认为更好的O(1)解决方案如下(,如果你的枚举是Int从x开始等等):

enum Test : Int {
    case ONE = 1
    case TWO
    case THREE
    case FOUR // if you later need to add additional enums add above COUNT so COUNT is always the last enum value 
    case COUNT

    static var count: Int { return Test.COUNT.rawValue } // note if your enum starts at 0, some other number, etc. you'll need to add on to the raw value the differential 
}

我仍然认为当前选择的答案是所有枚举的最佳答案,除非您正在使用Int,否则我建议使用此解决方案。