如何确定Swift枚举中的个案数?
(我希望避免使用manually enumerating through all the values,或使用旧的" enum_count trick"如果可能的话。)
答案 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
是从无效的案例索引中进行了位,则其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
答案 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
),因为它是一个严重碎片化的泛型类型,缺少很多功能。 1}}不再适用于Swift 3。
请注意,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
,否则我建议使用此解决方案。