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?

Isuru
źródło
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)
}
Kod inny
źródło
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).")
}
Sh_Khan
źródło
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"]
maxwell
źródło
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.

Frank Valbuena
źródło
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

Daniel Kuta
źródło
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”]

Vladimir Pchelyakov
źródło
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().

吖 奇 说 の archywillhe
źródło
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

Christopher Larsen
źródło
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)
Jean-Nicolas Defossé
źródło
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
mclam
źródło
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)
}
Carlos Chaguendo
źródło
Ł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!

Ben Patch
źródło
0

Rozszerzenie na wyliczenie, aby utworzyć allValues.

extension RawRepresentable where Self: CaseIterable {
      static var allValues: [Self.RawValue] {
        return self.allCases.map { $0.rawValue}
      }
    }
Ankit garg
źródło
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