Swift列挙型のケースの数を確認するにはどうすればよいですか?
( すべての値を手動で列挙する を避けたい、または可能であれば古い " enum_count trick "を使用する)
Swift 4.2(Xcode 10)以降、CaseIterable
プロトコルへの準拠を宣言できます。これは、値が関連付けられていないすべての列挙に対して機能します。
enum Stuff: CaseIterable {
case first
case second
case third
case forth
}
ケースの数は、次のようにして簡単に取得できます。
print(Stuff.allCases.count) // 4
詳細については、
ブログ投稿 がありますが、これについて詳しく説明しますが、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
}()
}
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を返します。
それがルールなのか、将来変わるのかはわからないので、ご自身の責任で使用してください :)
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)
この answer に示すように、静的なallValues配列を作成します
enum ProductCategory : String {
case Washers = "washers", Dryers = "dryers", Toasters = "toasters"
static let allValues = [Washers, Dryers, Toasters]
}
...
let count = ProductCategory.allValues.count
これは、値を列挙するときにも役立ち、すべてのEnumタイプで機能します
実装に整数列挙型の使用に反対するものがない場合は、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!")
}
}
この種の関数は、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
}
ああ、みなさん、単体テストはどうですか?
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
の実装を更新しない場合.
インデックス付き文字列列挙
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
楽しい :)
この関数は、文書化されていない2つのcurrent(Swift 1.1)enum
の動作に依存しています。
enum
のメモリレイアウトは、単にcase
のインデックスです。ケースカウントが2〜256の場合、UInt8
です。enum
がinvalid case indexからビットキャストされた場合、そのhashValue
は0
です自己責任で使用してください:)
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
生の値が整数であるすべての列挙型に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が投稿したアプローチに非常に似ていますが、すべての整数型で機能します。
もちろん、動的ではありませんが、多くの用途では、Enumに静的変数を追加することで取得できます
static var count: Int{ return 7 }
それをEnumName.count
として使用します
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
なぜそんなに複雑にしているのですか? Int列挙型のSIMPLESTカウンターは以下を追加します:
case Count
最終的には。そして...ビオラ-カウントができました-速くて簡単
私のユースケースでは、複数の人が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を使用するオーバーヘッドを回避できます。
コードを最後の列挙に基づいたくない場合は、列挙内にこの関数を作成できます。
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
}
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で既に述べたUITableViews
のsection.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
}
プロトコル(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
}
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)")
次のメソッドは 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
を呼び出すだけです。
または、列挙体の外で_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
がそれを行う場合、この回答を削除します)