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?


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

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

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"]

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.


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


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


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é"]


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


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)

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

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

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!


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

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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.