Swift enumundaki vaka sayısını nasıl belirleyebilirim?
( Manuel olarak tüm değerler arasında numaralandırma veya mümkünse eski " enum_count hile " kullanarak önlemek istiyorum.)
Swift enumundaki vaka sayısını nasıl belirleyebilirim?
( Manuel olarak tüm değerler arasında numaralandırma veya mümkünse eski " enum_count hile " kullanarak önlemek istiyorum.)
Yanıtlar:
Swift 4.2'den (Xcode 10) itibaren CaseIterable
protokole uygunluk beyan edebilirsiniz , bu ilişkili değerler olmadan tüm numaralandırmalar için çalışır:
enum Stuff: CaseIterable {
case first
case second
case third
case forth
}
Vaka sayısı artık basitçe
print(Stuff.allCases.count) // 4
Daha fazla bilgi için, bkz
Bu konuda daha ayrıntılı bir blog yazısı var, ama enum ham türü bir tamsayı olduğu sürece, bu şekilde bir sayı ekleyebilirsiniz:
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
}()
}
case A=1, B=3
?
enum
sahip olmanın yanı sıra 2 varsayım vardır Int
: Int ham değerlerine sahip Swift enum'larının 0'dan başlaması gerekmez (varsayılan davranış olmasına rağmen) ve ham değerleri keyfi olabilir, (varsayılan davranış bu olsa da) 1 artırılır.
Xcode 10 güncellemesi
Enumda CaseIterable
protokolü benimseyin, allCases
tüm enum vakalarını içeren statik bir özellik sağlar Collection
. count
Enumun kaç vakanın olduğunu bilmek için mülkünü kullanmanız yeterlidir.
Bir örnek için Martin'in cevabına bakın (ve benimkinden çok cevaplarını oylayın)
Uyarı : Aşağıdaki yöntem artık çalışmıyor gibi görünüyor.
Enum vakalarının sayısını saymak için herhangi bir genel yöntemin farkında değilim. Ancak hashValue
, enum davalarının özelliğinin sıfırdan başlayarak ve davaların bildirildiği sıra ile belirlenen sıra ile arttığını fark ettim . Yani, son enum artı bir hash vaka sayısına karşılık gelir.
Örneğin bu numaralandırma ile:
enum Test {
case ONE
case TWO
case THREE
case FOUR
static var count: Int { return Test.FOUR.hashValue + 1}
}
count
4 döndürür.
Bunun bir kural olup olmadığını veya gelecekte değişip değişmeyeceğini söyleyemem, bu yüzden kendi sorumluluğunuzda kullanın :)
hashValues
Bu şeylere gerçekten güvenmemeliyiz ; Bildiğimiz tek şey, bunun rastgele benzersiz bir değer olduğunu - bazı derleyici uygulama ayrıntılarına bağlı olarak gelecekte çok kolay değişebileceğini; ancak genel olarak, yerleşik sayma işlevselliğinin eksikliği rahatsız edicidir.
case ONE = 0
, o zaman yerini alabilir hashValue
ile rawValue
.
static var count = 4
Kaderinizin Swift
Nate Cook tarafından yayınlanan yaklaşıma göre vaka sayısını otomatik olarak gerçekleştiren yeniden kullanılabilir bir protokol tanımlarım.
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
}
}
Sonra bu protokolü aşağıdaki gibi yeniden kullanabilirsiniz:
enum Planet : Int, CaseCountable {
case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
}
//..
print(Planet.caseCount)
count++
için count+=1
çünkü ++
notasyonu Swift 3 kaldırılacaktır
static var caseCount: Int { get }
mıydı? neden ihtiyaç static func
?
case A=1, B=3
?
0
ve hiç boşluğa sahip olmamasını gerektirir .
Bu yanıtta gösterildiği gibi statik allValues dizisi oluşturun
enum ProductCategory : String {
case Washers = "washers", Dryers = "dryers", Toasters = "toasters"
static let allValues = [Washers, Dryers, Toasters]
}
...
let count = ProductCategory.allValues.count
Bu, değerleri numaralandırmak istediğinizde de yararlıdır ve tüm Enum türleri için çalışır
static let count = allValues.count
. Sonra isterseniz allValues
özel yapabilirsiniz.
Uygulamanın tamsayı numaralandırmalarını kullanmaya karşı hiçbir şey yoksa Count
, numaralandırmadaki üye sayısını temsil etmek için çağrılan fazladan bir üye değeri ekleyebilirsiniz - aşağıdaki örneğe bakın:
enum TableViewSections : Int {
case Watchlist
case AddButton
case Count
}
Şimdi numarayı kullanarak TableViewSections.Count.rawValue
yukarıdaki örnek için 2 döndürecek üye sayısını alabilirsiniz .
Enum'u bir switch deyimiyle işlerken, üyeyle Count
beklemediğiniz bir yerde karşılaşırken bir onaylama hatası attığınızdan emin olun :
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!")
}
}
Bu tür bir işlev, numaralandırma sayınızı geri döndürebilir.
Hızlı 2 :
func enumCount<T: Hashable>(_: T.Type) -> Int {
var i = 1
while (withUnsafePointer(&i) { UnsafePointer<T>($0).memory }).hashValue != 0 {
i += 1
}
return i
}
Hızlı 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
}
enum
olan da Hashable
aynı türden.
İndeksli String Enum
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)!
}
}
Miktar : eEventTabType.allValues.count
endeksi: objeEventTabType.index
Zevk almak :)
Hey hey millet, ya birim testleri?
func testEnumCountIsEqualToNumberOfItemsInEnum() {
var max: Int = 0
while let _ = Test(rawValue: max) { max += 1 }
XCTAssert(max == Test.count)
}
Bu Antonio'nun çözümü ile birleşti:
enum Test {
case one
case two
case three
case four
static var count: Int { return Test.four.hashValue + 1}
}
ana kodda size O (1) verir ve ayrıca bir numaralandırma durumu ekler ve uygulamasını güncellemezse başarısız bir test alırsınız .five
count
Bu işlev 2 belgelenmemiş akım (Swift 1.1) enum
davranışına dayanır :
enum
sadece bir indeks case
. Vaka sayısı 2'den 256'ya ise, öyle UInt8
.enum
oldu dan bit döküm geçersiz durum endeksi, onun hashValue
DİR0
Bu yüzden kendi sorumluluğunuzdadır kullanın :)
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")
}
}
Kullanımı:
enum Foo:String {
case C000 = "foo"
case C001 = "bar"
case C002 = "baz"
}
enumCaseCount(Foo) // -> 3
Ham değeri tamsayı bir count
özellik olduğu tüm numaralandırmalar veren basit bir uzantı yazdım :
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())
}
}
Ne yazık ki count
özelliği OptionSetType
düzgün çalışmadığı yere verir , bu yüzden CaseCountable
saymak istediğiniz durumlar için herhangi bir numaralandırma için protokole açık bir uyumluluk gerektiren başka bir sürüm :
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 tarafından yayınlanan yaklaşıma çok benzer, ancak tüm tamsayı türleriyle çalışır.
Tabii ki, dinamik değil ama birçok kullanım için Enum'unuza eklenen statik bir var
static var count: Int{ return 7 }
ve sonra onu EnumName.count
enum EnumNameType: Int {
case first
case second
case third
static var count: Int { return EnumNameType.third.rawValue + 1 }
}
print(EnumNameType.count) //3
VEYA
enum EnumNameType: Int {
case first
case second
case third
case count
}
print(EnumNameType.count.rawValue) //3
* Swift 4.2'de (Xcode 10) şunları kullanabilirsiniz:
enum EnumNameType: CaseIterable {
case first
case second
case third
}
print(EnumNameType.allCases.count) //3
Kullanım durumum için, birden çok kişinin bir numaralandırmaya anahtar ekleyebileceği bir kod tabanında ve bu durumların tümü allKeys özelliğinde kullanılabilir olmalıdır, allKey'lerin numaralandırmadaki anahtarlara karşı doğrulanması önemlidir. Bu, birisinin anahtarlarını tüm anahtarlar listesine eklemeyi unutmasını önlemek içindir. AllKeys dizisinin sayımını (ilk olarak yinelemeleri önlemek için bir dizi olarak oluşturulan) numaralandırmadaki anahtar sayısıyla eşleştirmek bunların hepsinin var olmasını sağlar.
Yukarıdaki cevaplardan bazıları Swift 2'de bunu başarmanın yolunu gösterir, ancak Swift 3'te hiçbiri işe yaramaz . İşte Swift 3 formatlı sürüm:
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)
}
Kullanım durumunuza bağlı olarak, her istekte allKeys kullanmanın yükünü önlemek için testi geliştirme aşamasında çalıştırmak isteyebilirsiniz.
Neden hepsini bu kadar karmaşık yapıyorsun? Int enum'un SIMPLEST sayacı şunları eklemektir:
case Count
Sonunda. Ve ... viyola - şimdi sayım var - hızlı ve basit
0
ve dizide boşluk olmaması gerekir.
Kodunuzu son numaraya dayandırmak istemiyorsanız, bu işlevi numaralandırmanızın içinde oluşturabilirsiniz.
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
}
Tür numaralarıyla çalışan bir Swift 3 sürümü 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
}
}
Kredi: bzz ve Nate Cook'un cevaplarına dayanarak.
Jenerik IntegerType
(yeniden adlandırılan Swift 3'te Integer
) pek çok işlevi olmayan ağır parçalanmış bir jenerik tip olduğu için desteklenmez. successor
Swift 3'te artık mevcut değil.
Kod Komutanı'ndan Nate Cooks'a verilen cevabın hala geçerli olduğunu unutmayın:
Bir değeri sabit kodlamanız gerekmediği için güzel olsa da, her çağrıldığında her numaralandırma değerini başlatır. O (1) yerine O (n) 'dir.
Bildiğim kadarıyla, statik tipte genel tiplerde desteklenmediği için protokol uzantısı olarak (ve Nate Cook gibi her numaralamaya uygulama yapmıyorsanız) şu anda bir çözüm yoktur.
Her neyse, küçük numaralandırmalar için bu bir sorun olmamalı. Tipik bir kullanım durumu olurdu section.count
için UITableViews
zaten Zorayr tarafından belirtildiği gibi.
Matthieu Riegler cevabını genişleten bu, Swift 3 için jeneriklerin kullanılmasını gerektirmeyen bir çözümdür ve enum tipini kullanarak kolayca çağrılabilir 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
}
Ben bir protokol (EnumIntArray) ve herhangi bir numaralandırma (hızlı 1.2 kullanarak) bir "All" değişkeni eklemek için çok kolay bir genel yarar işlevi (enumIntArray) oluşturarak kendim için bu sorunu çözdü. "All" değişkeni, numaralandırmadaki all.count komutunu kullanabilmeniz için numaralandırmadaki tüm öğelerin bir dizisini içerecektir
Yalnızca Int türünde ham değerler kullanan numaralandırmalarla çalışır, ancak diğer türler için de ilham verebilir.
Ayrıca yukarıda ve başka yerlerde okuduğum "numaralandırma boşluğu" ve "yineleme için aşırı zaman" konularına da değiniyor.
Fikir, EnumIntArray protokolünü numaralandırmanıza eklemek ve sonra enumIntArray işlevini çağırarak bir "all" statik değişkeni tanımlamak ve ilk öğeyi (ve numaralandırmada boşluklar varsa sonuncusu) sağlamaktır.
Statik değişken yalnızca bir kez başlatıldığından, tüm ham değerlerden geçme yükü programınıza yalnızca bir kez vurur.
örnek (boşluklar olmadan):
enum Animals:Int, EnumIntArray
{
case Cat=1, Dog, Rabbit, Chicken, Cow
static var all = enumIntArray(Animals.Cat)
}
örnek (boşluklu):
enum Animals:Int, EnumIntArray
{
case Cat = 1, Dog,
case Rabbit = 10, Chicken, Cow
static var all = enumIntArray(Animals.Cat, Animals.Cow)
}
İşte onu uygulayan kod:
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
}
Veya yalnızca _count
numaralandırmanın dışını tanımlayabilir ve statik olarak ekleyebilirsiniz:
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
}
Bu şekilde kaç tane numara oluşturursanız oluşturun, yalnızca bir kez oluşturulur.
(eğer öyleyse bu cevabı silin static
)
Aşağıdaki yöntem CoreKit'ten gelir ve bazılarının önerdiği yanıtlara benzer. Bu Swift 4 ile çalışır.
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
}
O zaman sadece araman gerek Weekdays.allValues.count
.
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)")
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
ancak numarasız türlerde kullanım konusunda dikkatli olun. Bazı geçici çözümler şunlar olabilir:
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
Numaralandırmanın son değerini artı birini içeren statik bir sabit kullanabilir.
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()
}
}
}
Bu küçük, ama daha iyi bir O (1) çözümü aşağıdaki olacağını düşünüyorum ( SADECE enum Int
x, vb. Başlıyorsa):
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
}
Halihazırda tüm cevaplar için en iyi cevap olduğuna inandığım seçili cevap, birlikte çalışmadığınız sürece Int
bu çözümü tavsiye ederim.
guard
karşı olan COUNT
ve hata veren, yanlış döndüren vb. Numaralandırma değerini doğrulayan bir işlev ekleyebilirsiniz .