Jak pobrać wszystkie wartości wyliczenia jako tablicę


103

Mam następujący wyliczenie.

enum EstimateItemStatus: Printable {
    case Pending
    case OnHold
    case Done

    var description: String {
        switch self {
        case .Pending: return "Pending"
        case .OnHold: return "On Hold"
        case .Done: return "Done"
        }
    }

    init?(id : Int) {
        switch id {
        case 1:
            self = .Pending
        case 2:
            self = .OnHold
        case 3:
            self = .Done
        default:
            return nil
        }
    }
}

Muszę pobrać wszystkie surowe wartości jako tablicę ciągów (tak jak to ["Pending", "On Hold", "Done"]).

Dodałem tę metodę do wyliczenia.

func toArray() -> [String] {
    var n = 1
    return Array(
        GeneratorOf<EstimateItemStatus> {
            return EstimateItemStatus(id: n++)!.description
        }
    )
}

Ale pojawia się następujący błąd.

Nie można znaleźć inicjatora dla typu „GeneratorOf”, który akceptuje listę argumentów typu „(() -> _)”

Czy jest na to łatwiejszy, lepszy lub bardziej elegancki sposób?


2
możesz utworzyć tablicę taką jak let array: [EstimateItemStatus] = [.Pending, .Onhold, .Done]
— Kristijan Delivuk

1
@KristijanDelivuk Chcę dodać tę funkcjonalność do samego wyliczenia. Więc nie muszę iść i dodawać go wszędzie w innych miejscach baz kodów, jeśli kiedykolwiek dodam inną wartość do wyliczenia.
— Isuru,


Mam odpowiedź, do której możesz odwołać się tutaj stackoverflow.com/a/48960126/5372480
— MSimic

Odpowiedzi:


155

Dla Swift 4.2 (Xcode 10) i nowszych

Jest CaseIterableprotokół:

enum EstimateItemStatus: String, CaseIterable {
    case pending = "Pending"
    case onHold = "OnHold"
    case done = "Done"

    init?(id : Int) {
        switch id {
        case 1: self = .pending
        case 2: self = .onHold
        case 3: self = .done
        default: return nil
        }
    }
}

for value in EstimateItemStatus.allCases {
    print(value)
}

Dla Swift <4,2

Nie, nie możesz zapytać, enumjakie wartości zawiera. Zobacz ten artykuł . Musisz zdefiniować tablicę zawierającą wszystkie posiadane wartości. Sprawdź również rozwiązanie Franka Valbueny w artykule „ Jak uzyskać wszystkie wartości wyliczenia jako tablicę ”.

enum EstimateItemStatus: String {
    case Pending = "Pending"
    case OnHold = "OnHold"
    case Done = "Done"

    static let allValues = [Pending, OnHold, Done]

    init?(id : Int) {
        switch id {
        case 1:
            self = .Pending
        case 2:
            self = .OnHold
        case 3:
            self = .Done
        default:
            return nil
        }
    }
}

for value in EstimateItemStatus.allValues {
    print(value)
}

Zobacz tę odpowiedź: stackoverflow.com/a/28341290/8047, w tym kod Swift 3.
— Dan Rosenstark

3
Głosowanie za częścią allValues, ale nie wiem, co sądzić o wyliczeniu będącym typem String, ale zainicjowanym przez Int.
— Tyress

Pierwszy link jest uszkodzony, ale wydaje się być w exceptionshub.com/... teraz.
— Tin Man

40

Swift 4.2 wprowadza nowy protokół o nazwieCaseIterable

enum Fruit : CaseIterable {
    case apple , apricot , orange, lemon
}

że kiedy się dostosujesz, możesz uzyskać tablicę z takich enumprzypadków

for fruit in Fruit.allCases {
    print("I like eating \(fruit).")
}

27

Dodaj protokół CaseIterable do wyliczenia:

enum EstimateItemStatus: String, CaseIterable {
    case pending = "Pending"
    case onHold = "OnHold"
    case done = "Done"
}

Stosowanie:

let values: [String] = EstimateItemStatus.allCases.map { $0.rawValue }
//["Pending", "OnHold", "Done"]

17

Jest jeszcze jeden sposób, który przynajmniej jest bezpieczny w czasie kompilacji:

enum MyEnum {
    case case1
    case case2
    case case3
}

extension MyEnum {
    static var allValues: [MyEnum] {
        var allValues: [MyEnum] = []
        switch (MyEnum.case1) {
        case .case1: allValues.append(.case1); fallthrough
        case .case2: allValues.append(.case2); fallthrough
        case .case3: allValues.append(.case3)
        }
        return allValues
    }
}

Zauważ, że działa to dla każdego typu wyliczenia (RawRepresentable lub nie), a także jeśli dodasz nowy przypadek, otrzymasz błąd kompilatora, który jest dobry, ponieważ zmusi Cię do zaktualizowania tego.


1
Nieortodoksyjne, ale działa i ostrzega, jeśli zmodyfikujesz przypadki wyliczenia. Sprytne rozwiązanie!
— Chuck Krutsinger

12

Znalazłem gdzieś ten kod:

protocol EnumCollection : Hashable {}


extension EnumCollection {

    static func cases() -> AnySequence<Self> {
        typealias S = Self
        return AnySequence { () -> AnyIterator<S> in
            var raw = 0
            return AnyIterator {
                let current : Self = withUnsafePointer(to: &raw) { $0.withMemoryRebound(to: S.self, capacity: 1) { $0.pointee }
                }
                guard current.hashValue == raw else { return nil }
                raw += 1
                return current
            }
        }
    }
}

Posługiwać się:

enum YourEnum: EnumCollection { //code }

YourEnum.cases()

lista zwrotów spraw z YourEnum


Wygląda na świetne rozwiązanie, ale ma sporo błędów kompilacji w Swift 4.
— Isuru

1
„Gdzieś” może być: theswiftdev.com/2017/10/12/swift-enum-all-values (między innymi?). Bloger wskazuje na CoreKit .
— AmitaiB

5
To psuje się w XCode 10 (niezależnie od wersji Swift), ponieważ hashValue wyliczenia nie jest już przyrostowe, ale losowe, co zrywa mechanizm. Nowym sposobem na to jest aktualizacja do Swift 4.2 i użycie CaseIterable
— Yasper

10
enum EstimateItemStatus: String, CaseIterable {
  case pending = "Pending"
  case onHold = "OnHold"
  case done = "Done"

  static var statusList: [String] {
    return EstimateItemStatus.allCases.map { $0.rawValue }
  }
}

[„Oczekujące”, „Wstrzymane”, „Gotowe”]


8

Aby uzyskać listę do celów funkcjonalnych, użyj wyrażenia, EnumName.allCasesktóre zwraca tablicę np

EnumName.allCases.map{$0.rawValue} 

poda ci listę ciągów znaków, które to podano EnumName: String, CaseIterable

Uwaga: użyj allCaseszamiast AllCases().


2

Aktualizacja dla Swift 5

Najłatwiejszym rozwiązaniem, jakie znalazłem, jest użycie .allCasesna wyliczeniu, które rozszerzaCaseIterable

enum EstimateItemStatus: CaseIterable {
    case Pending
    case OnHold
    case Done

    var description: String {
        switch self {
        case .Pending: return "Pending"
        case .OnHold: return "On Hold"
        case .Done: return "Done"
        }
    }

    init?(id : Int) {
        switch id {
        case 1:
            self = .Pending
        case 2:
            self = .OnHold
        case 3:
            self = .Done
        default:
            return nil
        }
    }
}

.allCasesna każdym CaseIterablewyliczeniu zwróci a Collectionz tego elementu.

var myEnumArray = EstimateItemStatus.allCases

więcej informacji o CaseIterable


Nie ma potrzeby implementacji opisu (). Wystarczy zrównać każdy przypadek z ciągiem znaków, np. case OnHold = "On Hold"I stanie się on wartością surową dla każdego.
— pnizzle

@pnizzle Wiem, jest tam, ponieważ było w oryginalnym pytaniu.
— Christopher Larsen

1

Dla Swift 2

// Found http://stackoverflow.com/questions/24007461/how-to-enumerate-an-enum-with-string-type
func iterateEnum<T where T: Hashable, T: RawRepresentable>(_: T.Type) -> AnyGenerator<T> {
    var i = 0
    return AnyGenerator {
        let next = withUnsafePointer(&i) {
            UnsafePointer<T>($0).memory
        }
        if next.hashValue == i {
            i += 1
            return next
        } else {
            return nil
        }
    }
}

func arrayEnum<T where T: Hashable, T: RawRepresentable>(type: T.Type) -> [T]{
    return Array(iterateEnum(type))
}

Aby z niego skorzystać:

arrayEnum(MyEnumClass.self)

Dlaczego elementy miałyby hashValuebyć 0..n?
— NRitH

1

Po inspiracji z Sekwencji i godzinach prób n błędów. W końcu dostałem ten wygodny i piękny Swift 4 Way na Xcode 9.1:

protocol EnumSequenceElement: Strideable {
    var rawValue: Int { get }
    init?(rawValue: Int)
}

extension EnumSequenceElement {
    func distance(to other: Self) -> Int {
        return other.rawValue - rawValue
    }

    func advanced(by n: Int) -> Self {
        return Self(rawValue: n + rawValue) ?? self
    }
}

struct EnumSequence<T: EnumSequenceElement>: Sequence, IteratorProtocol {
    typealias Element = T

    var current: Element? = T.init(rawValue: 0)

    mutating func next() -> Element? {
        defer {
            if let current = current {
                self.current = T.init(rawValue: current.rawValue + 1)
            }
        }
        return current
    }
}

Stosowanie:

enum EstimateItemStatus: Int, EnumSequenceElement, CustomStringConvertible {
    case Pending
    case OnHold
    case Done

    var description: String {
        switch self {
        case .Pending:
            return "Pending"
        case .OnHold:
            return "On Hold"
        case .Done:
            return "Done"
        }
    }
}

for status in EnumSequence<EstimateItemStatus>() {
    print(status)
}
// Or by countable range iteration
for status: EstimateItemStatus in .Pending ... .Done {
    print(status)
}

Wynik:

Pending
On Hold
Done

1

Możesz użyć

enum Status: Int{
    case a
    case b
    case c

}

extension RawRepresentable where Self.RawValue == Int {

    static var values: [Self] {
        var values: [Self] = []
        var index = 1
        while let element = self.init(rawValue: index) {
            values.append(element)
            index += 1
        }
        return values
    }
}


Status.values.forEach { (st) in
    print(st)
}

Ładny! Po aktualizacji z wersji Swift 3.2 do 4.1 było to rozwiązanie, z którego korzystałem. Pierwotnie mieliśmy deklaracje AnyItertor <Self>. Twoje rozwiązanie było dużo bardziej przejrzyste i czytelne. dzięki!
— Nick N

2
Jednak jeden błąd w kodzie. Brakuje pierwszego przedmiotu w etui. Zmień var index = 1 na var index = 0
— Nick N

0

Jeśli wyliczenie jest przyrostowe i skojarzone z liczbami, możesz użyć zakresu liczb, które są mapowane na wartości wyliczeniowe, na przykład:

// Swift 3
enum EstimateItemStatus: Int {
    case pending = 1,
    onHold
    done
}

let estimateItemStatusValues: [EstimateItemStatus?] = (EstimateItemStatus.pending.rawValue...EstimateItemStatus.done.rawValue).map { EstimateItemStatus(rawValue: $0) }

To nie działa w przypadku wyliczeń skojarzonych z ciągami znaków lub czymkolwiek innym niż liczby, ale działa świetnie, jeśli tak jest!


0

Rozszerzenie na wyliczenie, aby utworzyć allValues.

extension RawRepresentable where Self: CaseIterable {
      static var allValues: [Self.RawValue] {
        return self.allCases.map { $0.rawValue}
      }
    }

Chociaż ten kod może stanowić rozwiązanie problemu OP, zdecydowanie zaleca się podanie dodatkowego kontekstu wyjaśniającego, dlaczego i / lub jak ten kod odpowiada na pytanie. Odpowiedzi zawierające tylko kod zazwyczaj stają się bezużyteczne na dłuższą metę, ponieważ przyszli widzowie doświadczający podobnych problemów nie mogą zrozumieć przyczyny rozwiązania.
— E. Zeytinci
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.