Comment obtenir toutes les valeurs d'énumération sous forme de tableau

101

J'ai l'énumération suivante.

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

J'ai besoin d'obtenir toutes les valeurs brutes sous forme de tableau de chaînes (comme ça ["Pending", "On Hold", "Done"]).

J'ai ajouté cette méthode à l'énumération.

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

Mais j'obtiens l'erreur suivante.

Impossible de trouver un initialiseur pour le type 'GeneratorOf' qui accepte une liste d'arguments de type '(() -> _)'

Existe-t-il une manière plus simple, meilleure ou plus élégante de le faire?

Isuru
la source
2
vous pouvez créer un tableau comme let array: [EstimateItemStatus] = [.Pending, .Onhold, .Done]
Kristijan Delivuk
1
@KristijanDelivuk Je souhaite ajouter cette fonctionnalité à l'énumération elle-même. Je n'ai donc pas besoin d'aller l'ajouter partout dans d'autres endroits des bases de code si jamais j'ajoute une autre valeur à l'énumération.
Isuru
J'ai une réponse à laquelle vous pouvez vous référer ici stackoverflow.com/a/48960126/5372480
MSimic

Réponses:

149

Pour Swift 4.2 (Xcode 10) et versions ultérieures

Il y a un CaseIterableprotocole:

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

Pour Swift <4.2

Non, vous ne pouvez pas interroger un enumpour les valeurs qu'il contient. Consultez cet article . Vous devez définir un tableau qui répertorie toutes les valeurs que vous avez. Consultez également la solution de Frank Valbuena dans " Comment obtenir toutes les valeurs d'énumération sous forme de tableau ".

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 différent
la source
Consultez cette réponse: stackoverflow.com/a/28341290/8047, y compris le code Swift 3.
Dan Rosenstark
3
Vote positif pour la partie allValues, mais je ne sais pas trop quoi penser d'une énumération étant de type String mais initialisée avec Int.
Tyress
Le premier lien est rompu mais semble être sur exceptionshub.com/ ... maintenant.
the Tin Man le
39

Swift 4.2 introduit un nouveau protocole nomméCaseIterable

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

que lorsque vous vous conformez à, vous pouvez obtenir un tableau à partir des enumcas comme celui-ci

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

Ajoutez le protocole CaseIterable à enum:

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

Usage:

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

Il existe au moins un autre moyen sûr au moment de la compilation:

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

Notez que cela fonctionne pour tout type d'énumération (RawRepresentable ou non) et aussi si vous ajoutez un nouveau cas, vous obtiendrez une erreur de compilation qui est bonne car vous obligera à l'avoir à jour.

Frank Valbuena
la source
1
Peu orthodoxe, mais cela fonctionne et il vous avertit si vous modifiez les cas d'énumération. Solution intelligente!
Chuck Krutsinger
12

J'ai trouvé quelque part ce code:

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

Utilisation:

enum YourEnum: EnumCollection { //code }

YourEnum.cases()

renvoyer la liste des cas de YourEnum

Daniel Kuta
la source
Semble être une excellente solution mais a pas mal d'erreurs de compilation sur Swift 4.
Isuru
1
Le "quelque part" peut être: theswiftdev.com/2017/10/12/swift-enum-all-values (entre autres?). Le blogueur crédite CoreKit .
AmitaiB
5
Cela se brise dans XCode 10 (quelle que soit la version de Swift) car la valeur de hachage d'une énumération n'est plus incrémentielle mais aléatoire, cassant le mécanisme. La nouvelle façon de faire est de passer à Swift 4.2 et d'utiliser CaseIterable
Yasper
8

Pour obtenir une liste à des fins fonctionnelles, utilisez l'expression EnumName.allCasesqui renvoie un tableau par exemple

EnumName.allCases.map{$0.rawValue} 

vous donnera une liste de chaînes étant donné que EnumName: String, CaseIterable

Remarque: utilisez à la allCasesplace de AllCases().

吖 奇 说 - 何魏奇 Archy va-t-il
la source
8
enum EstimateItemStatus: String, CaseIterable {
  case pending = "Pending"
  case onHold = "OnHold"
  case done = "Done"

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

["En attente", "En attente", "Terminé"]

Vladimir Pchelyakov
la source
2

Mise à jour pour Swift 5

La solution la plus simple que j'ai trouvée est d'utiliser .allCasessur une énumération qui s'étendCaseIterable

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

.allCasessur n'importe quelle CaseIterableénumération retournera un Collectionélément de cet élément.

var myEnumArray = EstimateItemStatus.allCases

plus d'infos sur CaseIterable

Christopher Larsen
la source
Il n'est pas nécessaire d'implémenter description (). Assimilez simplement chaque cas à la chaîne, par exemple, case OnHold = "On Hold"et cela devient la valeur brute de chacun.
pnizzle
@pnizzle Je sais, c'est là parce que c'était dans la question d'origine.
Christopher Larsen
1

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

Pour l'utiliser:

arrayEnum(MyEnumClass.self)
Jean-Nicolas Defossé
la source
Pourquoi les éléments hashValueseraient-ils 0..n?
NRitH
1

Après l'inspiration de la séquence et des heures d'erreurs d'essai. J'ai enfin eu cette façon confortable et belle Swift 4 sur 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
    }
}

Usage:

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

Production:

Pending
On Hold
Done
mclam
la source
1

Vous pouvez utiliser

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
la source
Agréable! Après la mise à niveau de Swift 3.2 vers 4.1, c'était une solution que j'ai utilisée. Nous avions à l'origine des déclarations AnyItertor <Self>. Votre solution était beaucoup plus propre et plus facile à lire. Merci!
Nick N
2
Un défaut de code ici cependant. Il manque le premier élément de l'affaire. Changer var index = 1 en var index = 0
Nick N
0

Si votre énumération est incrémentielle et associée à des nombres, vous pouvez utiliser une plage de nombres que vous mappez aux valeurs d'énumération, comme ceci:

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

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

Cela ne fonctionne pas tout à fait avec les énumérations associées à des chaînes ou à autre chose que des nombres, mais cela fonctionne très bien si tel est le cas!

Ben Patch
la source
0

Extension sur une énumération pour créer allValues.

extension RawRepresentable where Self: CaseIterable {
      static var allValues: [Self.RawValue] {
        return self.allCases.map { $0.rawValue}
      }
    }
Garg Ankit
la source
Bien que ce code puisse fournir une solution au problème d'OP, il est fortement recommandé de fournir un contexte supplémentaire concernant pourquoi et / ou comment ce code répond à la question. Les réponses au code uniquement deviennent généralement inutiles à long terme, car les futurs téléspectateurs rencontrant des problèmes similaires ne peuvent pas comprendre le raisonnement derrière la solution.
E. Zeytinci le