web-dev-qa-db-ja.com

Swift列挙型のカウントを取得するにはどうすればよいですか?

Swift列挙型のケースの数を確認するにはどうすればよいですか?

すべての値を手動で列挙する を避けたい、または可能であれば古い " enum_count trick "を使用する)

164
Robert Atkins

Swift 4.2(Xcode 10)以降、CaseIterableプロトコルへの準拠を宣言できます。これは、値が関連付けられていないすべての列挙に対して機能します。

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

ケースの数は、次のようにして簡単に取得できます。

print(Stuff.allCases.count) // 4

詳細については、

105
Martin R

ブログ投稿 がありますが、これについて詳しく説明しますが、enumの生の型が整数である限り、この方法でカウントを追加できます。

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
    }()
}
138
Nate Cook

Xcode 10アップデート

列挙型でCaseIterableプロトコルを採用し、すべての列挙型のケースをallCasesとして含む静的なCollectionプロパティを提供します。 countプロパティを使用して、enumのケース数を知るだけです。

例についてはMartinの回答を参照してください(そして、私のものではなく、彼の回答に賛成してください)


警告:以下の方法はもう機能しないようです。

Enumケースの数をカウントする一般的な方法を知りません。ただし、enumケースのhashValueプロパティは、ゼロから始まり、ケースが宣言された順序によって決定される順序で増分されることに気付きました。したがって、最後の列挙型に1を加えたハッシュは、ケースの数に対応します。

この列挙型の例:

enum Test {
    case ONE
    case TWO
    case THREE
    case FOUR

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

countは4を返します。

それがルールなのか、将来変わるのかはわからないので、ご自身の責任で使用してください :)

88
Antonio

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)
69
Tom Pelaia

この answer に示すように、静的なallValues配列を作成します

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

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

...

let count = ProductCategory.allValues.count

これは、値を列挙するときにも役立ち、すべてのEnumタイプで機能します

35
david72

実装に整数列挙型の使用に反対するものがない場合は、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!")
  }
}
15
Zorayr

この種の関数は、enumのカウントを返すことができます。

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

ああ、みなさん、単体テストはどうですか?

func testEnumCountIsEqualToNumberOfItemsInEnum() {

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

    XCTAssert(max == Test.count)
}

これは、アントニオのソリューションと組み合わされました。

enum Test {

    case one
    case two
    case three
    case four

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

メインコードでO(1) plus 失敗したテストを取得します誰かがenumケースfiveを追加し、countの実装を更新しない場合.

9
buildsucceeded

インデックス付き文字列列挙

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
kalpesh jetani

この関数は、文書化されていない2つのcurrent(Swift 1.1)enumの動作に依存しています。

  • enumのメモリレイアウトは、単にcaseのインデックスです。ケースカウントが2〜256の場合、UInt8です。
  • enuminvalid case indexからビットキャストされた場合、その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
7
rintaro

生の値が整数であるすべての列挙型に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が投稿したアプローチに非常に似ていますが、すべての整数型で機能します。

5
bzz

もちろん、動的ではありませんが、多くの用途では、Enumに静的変数を追加することで取得できます

static var count: Int{ return 7 }

それをEnumName.countとして使用します

4
Ian Dundas
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
3
Lê Cường

なぜそんなに複雑にしているのですか? Int列挙型のSIMPLESTカウンターは以下を追加します:

case Count

最終的には。そして...ビオラ-カウントができました-速くて簡単

2
Dimitar Marinov

私のユースケースでは、複数の人がenumにキーを追加することができ、allKeysプロパティでこれらのすべてのケースを使用できるコードベースで、allKeysがenumのキーに対して検証されることが重要です。 これは、誰かが自分のキーをすべてのキーのリストに追加するのを忘れないようにするためです。allKeys配列のカウントを一致させる)列挙内のキーの数に対して、それらがすべて存在することを保証します。

上記の回答のいくつかは、Swift 2でこれを達成する方法を示していますが、Swiftでは機能しません。 Swiftフォーマット済みバージョンは次のとおりです。

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を使用するオーバーヘッドを回避できます。

2

コードを最後の列挙に基づいたくない場合は、列挙内にこの関数を作成できます。

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
}
1
Gabriel Araujo

SwiftバージョンはIntタイプの列挙型で動作します:

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の回答に基づきます。

ジェネリックIntegerType(Swift 3 in Integerに名前変更)は、多くの機能を持たない非常に断片化されたジェネリック型であるため、サポートされていません。 successorはSwift 3では使用できなくなりました。

Code CommanderからNate Cooksの回答に対するコメントはまだ有効であることに注意してください。

値をハードコードする必要がないのでいいのですが、これは呼び出されるたびにすべての列挙値をインスタンス化します。これは、O(1)ではなくO(n)です。

私が知る限り、これをプロトコル拡張として使用するとき(およびネイトクックのように各列挙に実装しないとき)に、ジェネリック型でサポートされていない静的な保存プロパティのため、現在回避策はありません。

とにかく、小さな列挙型の場合、これは問題になりません。典型的なユースケースは、Zorayrで既に述べたUITableViewssection.countです。

Matthieu Rieglerの答えを拡張すると、これはSwiftの解決策であり、ジェネリックの使用を必要とせず、EnumType.elementsCountでenum型を使用して簡単に呼び出すことができます:

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

プロトコル(EnumIntArray)とグローバルユーティリティ関数(enumIntArray)を作成して、すべての列挙型に「All」変数を簡単に追加できるようにすることで、この問題を解決しました(Swift 1.2を使用)。 「all」変数には列挙型のすべての要素の配列が含まれるため、countにall.countを使用できます

Int型の生の値を使用する列挙型でのみ機能しますが、他の型のインスピレーションを提供できる可能性があります。

また、上記および他の場所で読んだ「番号付けのギャップ」と「反復するのに時間がかかりすぎる」問題にも対処します。

アイデアは、EnumIntArrayプロトコルを列挙に追加し、enumIntArray関数を呼び出して「すべて」の静的変数を定義し、最初の要素(および番号付けにギャップがある場合は最後の要素)を提供することです。

静的変数は1回だけ初期化されるため、すべての生の値を処理するオーバーヘッドはプログラムに1回しかヒットしません。

例(ギャップなし):

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   
}
0
Alain T.
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)")

0
Nupur Sharma

次のメソッドは CoreKit からのもので、他の人が提案した答えに似ています。これはSwift 4で機能します。

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
}

次に、Weekdays.allValues.countを呼び出すだけです。

0
ThomasW

または、列挙体の外で_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がそれを行う場合、この回答を削除します)

0
A T