Подтвердить что ты не робот

Как мне получить счет Swift?

Как определить количество случаев в перечислении Swift?

(Я хотел бы избежать вручную перечислять все значения или использовать старый трюк enum_count "если возможно.

4b9b3361

Ответ 1

Начиная с Swift 4.2 (Xcode 10) вы можете объявить соответствие протоколу CaseIterable, это работает для всех перечислений без связанных значений:

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

Теперь количество случаев просто получается с

print(Stuff.allCases.count) // 4

Для получения дополнительной информации см.

Ответ 2

У меня сообщение в блоге, которое более подробно описано на этом, но пока ваш тип перечислимого типа является целым числом, вы можете добавить счет таким образом:

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
    }()
}

Ответ 3

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.

Я не могу сказать, будет ли это правило или оно когда-либо изменится в будущем, так что используйте на свой страх и риск :)

Ответ 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)

Ответ 5

Создайте статический массив allValues, как показано в этом answer

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

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

...

let count = ProductCategory.allValues.count

Это также полезно, если вы хотите перечислить значения и работать для всех типов Enum

Ответ 6

Если реализация не имеет ничего против использования целочисленных перечислений, вы можете добавить дополнительное значение члена под названием 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!")
  }
}

Ответ 7

Эта функция может возвращать счет вашего перечисления.

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
   }

Ответ 8

О, эй, все, как насчет модульных тестов?

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) плюс , вы получаете неудачный тест, если кто-то добавляет случай перечисления five и не обновляет реализацию count.

Ответ 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)!
    }
}

count: eEventTabType.allValues.count

index: objeEventTabType.index

Наслаждайтесь:)

Ответ 10

Эта функция основана на 2 недокументированных действиях (Swift 1.1) enum:

  • Макет памяти enum - это всего лишь индекс case. Если число случаев от 2 до 256, оно UInt8.
  • Если enum был отброшен в битах из недопустимого индекса case, его 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

Ответ 11

Я написал простое расширение, которое дает все перечисления, где необработанное значение целое a 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())
    }
}

Он очень похож на подход, поставленный Томом Пелаей, но работает со всеми целыми типами.

Ответ 12

Конечно, он не динамический, но для многих применений вы можете получить статический var, добавленный в ваш Enum

static var count: Int{ return 7 }

а затем используйте его как EnumName.count

Ответ 13

enum EnumNameType: Int {
    case first
    case second
    case third

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

print(EnumNameType.count) //3

ИЛИ ЖЕ

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

Ответ 14

В моем случае использования в кодовой базе, где несколько человек могут добавлять ключи к перечислению, и все эти случаи должны быть доступны в свойстве 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 по каждому запросу

Ответ 15

Почему вы делаете все это настолько сложным? Счетчик SIMPLEST Int enum должен добавить:

case Count

В конце концов. И... viola - теперь у вас есть счет - быстрый и простой

Ответ 16

Если вы не хотите использовать свой код в последнем перечислении, вы можете создать эту функцию внутри своего перечисления.

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
}

Ответ 17

A версия Swift 3, работающая с перечислением типа 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, переименованный в Integer) не поддерживается, поскольку он сильно фрагментированный родовой тип, который не имеет большого количества функций. successor больше не доступен для Swift 3.

Имейте в виду, что комментарий от Code Commander к ответу Nate Cooks остается в силе:

Хорошо, потому что вам не нужно жестко указывать значение, это будет создавать каждое значение перечисления каждый раз, когда он вызывается. Это O (n) вместо O (1).

Насколько я знаю, в настоящее время нет обходного пути при использовании этого в качестве расширения протокола (а не реализации в каждом перечислении, например, Nate Cook) из-за того, что статические хранимые свойства не поддерживаются в общих типах.

В любом случае, для небольших перечислений это не должно быть проблемой. Типичным примером использования будет section.count для UITableViews, как уже упоминалось Зорайром.

Ответ 18

Расширение ответа 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
}

Ответ 19

Я решил эту проблему для себя, создав протокол (EnumIntArray) и глобальную функцию полезности (enumIntArray), благодаря которой очень легко добавить переменную "Все" в любое перечисление (с использованием swift 1.2). Переменная "all" будет содержать массив всех элементов в перечислении, чтобы вы могли использовать all.count для count

Он работает только с перечислениями, которые используют исходные значения типа Int, но, возможно, это может послужить источником вдохновения для других типов.

Он также рассматривает проблемы "пробел в нумерации" и "чрезмерное время для повторения", которые я прочитал выше и в других местах.

Идея состоит в том, чтобы добавить протокол EnumIntArray к вашему перечислению, а затем определить статическую переменную "all", вызвав функцию 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   
}

Ответ 20

Или вы можете просто определить _count вне enum и прикрепить его статически:

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)

Ответ 21

Следующий метод исходит от 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.

Ответ 22

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()

печать ( "(weekdays.count)")

Ответ 23

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

Ответ 24

Он может использовать статическую константу, которая содержит последнее значение перечисления плюс одно.

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()
        }
    }
}

Ответ 25

Это незначительно, но я думаю, что лучшее решение 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, тогда я рекомендую это решение.