So erhalten Sie alle Enum-Werte als Array

98

Ich habe die folgende Aufzählung.

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

Ich muss alle Rohwerte als Array von Strings erhalten (wie so ["Pending", "On Hold", "Done"]).

Ich habe diese Methode zur Aufzählung hinzugefügt.

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

Aber ich erhalte den folgenden Fehler.

Es kann kein Initialisierer für den Typ 'GeneratorOf' gefunden werden, der eine Argumentliste vom Typ '(() -> _)' akzeptiert.

Gibt es eine einfachere, bessere oder elegantere Möglichkeit, dies zu tun?

Isuru
quelle
2
Sie können ein Array wie let array erstellen: [EstimateItemStatus] = [.Pending, .Onhold, .Done]
Kristijan Delivuk
1
@KristijanDelivuk Ich möchte diese Funktionalität zur Aufzählung selbst hinzufügen. Ich muss es also nicht überall an anderen Stellen der Codebasen hinzufügen, wenn ich der Aufzählung jemals einen weiteren Wert hinzufügen möchte.
Isuru
Ich habe eine Antwort, die Sie hier finden können stackoverflow.com/a/48960126/5372480
MSimic

Antworten:

146

Für Swift 4.2 (Xcode 10) und höher

Es gibt ein CaseIterableProtokoll:

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

Für Swift <4.2

Nein, Sie können nicht abfragen, enumwelche Werte darin enthalten sind. Siehe diesen Artikel . Sie müssen ein Array definieren, das alle Ihre Werte auflistet. Lesen Sie auch die Lösung von Frank Valbuena unter " So erhalten Sie alle Enum-Werte als Array ".

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)
}
Code anders
quelle
Siehe diese Antwort: stackoverflow.com/a/28341290/8047 einschließlich des Swift 3-Codes.
Dan Rosenstark
3
Upvoting für den allValues-Teil, aber nicht sicher, was Sie von einer Aufzählung halten sollen, die vom Typ String ist, aber mit Int initialisiert wurde.
Tyress
Der erste Link ist defekt, scheint sich aber jetzt unter exceptionhub.com/… zu befinden .
Der Blechmann
38

Swift 4.2 führt ein neues Protokoll mit dem Namen einCaseIterable

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

Wenn Sie sich an die Anforderungen halten, können Sie aus enumsolchen Fällen ein Array abrufen

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

Fügen Sie das CaseIterable- Protokoll zur Aufzählung hinzu:

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

Verwendung:

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

Es gibt noch einen anderen Weg, der zumindest beim Kompilieren sicher ist:

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

Beachten Sie, dass dies für jeden Aufzählungstyp (RawRepresentable oder nicht) funktioniert. Wenn Sie einen neuen Fall hinzufügen, wird ein Compilerfehler angezeigt, der gut ist, da Sie gezwungen sind, diesen auf den neuesten Stand zu bringen.

Frank Valbuena
quelle
1
Unorthodox, aber es funktioniert und warnt Sie, wenn Sie die Enum-Fälle ändern. Clevere Lösung!
Chuck Krutsinger
12

Ich habe irgendwo diesen Code gefunden:

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

Verwenden:

enum YourEnum: EnumCollection { //code }

YourEnum.cases()

Liste der Fälle von YourEnum zurückgeben

Daniel Kuta
quelle
Scheint eine großartige Lösung zu sein, hat aber einige Kompilierungsfehler auf Swift 4.
Isuru
1
Das "irgendwo" kann sein: theswiftdev.com/2017/10/12/swift-enum-all-values (unter anderem?). Der Blogger schreibt CoreKit gut .
AmitaiB
5
Dies unterbricht XCode 10 (unabhängig von der Swift-Version), da der Hashwert einer Aufzählung nicht mehr inkrementell, sondern zufällig ist und den Mechanismus unterbricht. Die neue Möglichkeit, dies zu tun, besteht darin, auf Swift 4.2 zu aktualisieren und CaseIterable
Yasper
8

Verwenden Sie den Ausdruck, EnumName.allCasesder ein Array zurückgibt, z

EnumName.allCases.map{$0.rawValue} 

wird Ihnen eine Liste von Strings geben, wenn das gegeben ist EnumName: String, CaseIterable

Hinweis: Verwenden Sie allCasesanstelle von AllCases().

吖 奇 说 - 何魏奇 Archy Will He
quelle
8
enum EstimateItemStatus: String, CaseIterable {
  case pending = "Pending"
  case onHold = "OnHold"
  case done = "Done"

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

["Ausstehend", "OnHold", "Fertig"]

Vladimir Pchelyakov
quelle
2

Update für Swift 5

Die einfachste Lösung, die ich gefunden habe, besteht darin, sie .allCasesfür eine erweiterte Aufzählung zu verwendenCaseIterable

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

.allCasesBei jeder CaseIterableAufzählung wird ein CollectionElement dieses Elements zurückgegeben.

var myEnumArray = EstimateItemStatus.allCases

Weitere Informationen zu CaseIterable

Christopher Larsen
quelle
Es ist nicht erforderlich, description () zu implementieren. Setzen Sie einfach jeden Fall mit der Zeichenfolge gleich, z. B. case OnHold = "On Hold"und das wird der Rohwert für jeden.
pnizzle
@pnizzle Ich weiß, es ist da, weil es in der ursprünglichen Frage war.
Christopher Larsen
1

Für 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))
}

Um es zu benutzen:

arrayEnum(MyEnumClass.self)
Jean-Nicolas Defossé
quelle
Warum sollten die Elemente hashValuesein 0..n?
NRitH
1

Nach Inspiration von Sequence und stundenlangem Versuch n Fehler. Ich habe endlich diesen komfortablen und schönen Swift 4 Way auf Xcode 9.1 bekommen:

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

Verwendung:

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

Ausgabe:

Pending
On Hold
Done
mclam
quelle
1

Sie können verwenden

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
quelle
Nett! Nach dem Upgrade von Swift 3.2 auf 4.1 war dies eine Lösung, die ich verwendet habe. Wir hatten ursprünglich AnyItertor <Self> -Deklarationen. Ihre Lösung war viel sauberer und leichter zu lesen. Vielen Dank!
Nick N
2
Ein Codefehler hier. Es fehlt der erste Punkt im Fall. Ändern Sie var index = 1 in var index = 0
Nick N
0

Wenn Ihre Aufzählung inkrementell ist und Zahlen zugeordnet ist, können Sie einen Zahlenbereich verwenden, den Sie Aufzählungswerten zuordnen, z.

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

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

Dies funktioniert nicht ganz mit Aufzählungen, die mit Zeichenfolgen oder anderen Elementen als Zahlen verknüpft sind, aber es funktioniert hervorragend, wenn dies der Fall ist!

Ben Patch
quelle
0

Erweiterung einer Aufzählung zum Erstellen von allValues.

extension RawRepresentable where Self: CaseIterable {
      static var allValues: [Self.RawValue] {
        return self.allCases.map { $0.rawValue}
      }
    }
Ankit garg
quelle
Dieser Code bietet möglicherweise eine Lösung für das OP-Problem. Es wird jedoch dringend empfohlen, einen zusätzlichen Kontext anzugeben, in dem erläutert wird, warum und / oder wie dieser Code die Frage beantwortet. Nur-Code-Antworten werden auf lange Sicht normalerweise unbrauchbar, da zukünftige Betrachter mit ähnlichen Problemen die Gründe für die Lösung nicht verstehen können.
E. Zeytinci