すべての列挙値を配列として取得する方法


99

次の列挙型があります。

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

すべての生の値を文字列の配列として取得する必要があります(のように["Pending", "On Hold", "Done"])。

このメソッドを列挙型に追加しました。

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

しかし、次のエラーが発生します。

タイプ '(()-> _)'の引数リストを受け入れるタイプ 'GeneratorOf'の初期化子が見つかりません

これを行うためのより簡単、より良い、またはよりエレガントな方法はありますか?


2
let array:[EstimateItemStatus] = [.Pending、.Onhold、.Done]のように配列を作成できます
Kristijan Delivuk

1
@KristijanDelivukこの機能を列挙型自体に追加します。したがって、列挙型に別の値を追加する場合は、コードベースの他の場所のどこにでも追加する必要はありません。
Isuru


私はここであなたが参照できる答えていstackoverflow.com/a/48960126/5372480
MSimic

回答:


146

Swift 4.2(Xcode 10)以降の場合

CaseIterableプロトコルがあります:

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

Swift <4.2の場合

いいえ、enum含まれている値をクエリすることはできません。この記事を参照してください。持つすべての値をリストする配列を定義する必要があります。また、「すべての列挙値を配列として取得する方法」のFrank Valbuenaの解決策も確認してください。

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

この回答を参照してください:Swift 3コードを含むstackoverflow.com/a/28341290/8047
Dan Rosenstark、2016

3
allValues部分に賛成票を投じますが、列挙型がStringであるがIntで初期化されているenumについてどう感じるかはわかりません。
タイレス、

最初のリンクは壊れていますが、exceptionshub.com /…にあるようです。
ティンマン

39

Swift 4.2は新しいプロトコルを導入しましたCaseIterable

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

に準拠すると、enumこのようなケースから配列を取得できること

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

25

列挙型にCaseIterableプロトコルを追加します。

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

使用法:

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

17

少なくともコンパイル時に安全である別の方法があります。

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

これは任意の列挙型(RawRepresentableであるかどうかにかかわらず)で機能し、新しいケースを追加するとコンパイラエラーが発生するので、これを強制的に最新にする必要があります。


1
正統ではありませんが、機能し、列挙型のケースを変更すると警告が表示されます。賢い解決策!
Chuck Krutsinger

12

私はどこかでこのコードを見つけました:

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

使用する:

enum YourEnum: EnumCollection { //code }

YourEnum.cases()

YourEnumからケースのリストを返す


素晴らしい解決策のように思えるが、スウィフト4にかなりの数のコンパイルエラーを持っている
Isuru

1
「どこか」は次のようになります:theswiftdev.com/2017/10/12/swift-enum-all-values(とりわけ)?ブロガーはCoreKitを信用しています。
AmitaiB

5
これは、列挙型のhashValueがインクリメンタルではなくランダムになるため、XCode 10では(Swiftバージョンに関係なく)機能しなくなり、メカニズムが機能しなくなります。これを行うための新しい方法は、スウィフト4.2にアップグレードしてCaseIterable使用することです
Yasper

8

関数の目的でリストを取得するにEnumName.allCasesは、配列を返す式を使用します。例:

EnumName.allCases.map{$0.rawValue} 

与えられた文字列のリストを与えます EnumName: String, CaseIterable

注:のallCases代わりに使用してください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 }
  }
}

[「保留中」、「保留中」、「完了」]


2

Swift 5のアップデート

私が見つけた最も簡単な解決策は.allCases、拡張する列挙で使用することですCaseIterable

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

.allCasesいずれにもCaseIterable列挙型を返しますCollectionその要素のを。

var myEnumArray = EstimateItemStatus.allCases

CaseIterableの詳細


description()を実装する必要はありません。たとえばcase OnHold = "On Hold"、各ケースを文字列と同等と見なし、それがそれぞれの生の値になります。
pnizzle

@pnizzle知っています、元の質問にあったのでそこにあります。
クリストファー・ラーセン

1

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

それを使用するには:

arrayEnum(MyEnumClass.self)

なぜ要素はhashValueあるの0..nでしょうか?
NRitH 2017

1

シーケンスからのインスピレーションと何時間もの試行後のエラー。Xcode 9.1で、この快適で美しいSwift 4の方法をようやく手に入れました。

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

使用法:

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

出力:

Pending
On Hold
Done

1

使用できます

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

いいね!Swift 3.2から4.1にアップグレードした後、これは私が使用したソリューションでした。最初はAnyItertor <Self>宣言がありました。あなたの解決策ははるかにすっきりとして読みやすくなりました。ありがとう!
Nick N

2
ただし、ここに1つのコードの欠陥があります。ケースの最初のアイテムがありません。var index = 1をvar index = 0に変更します
Nick N

0

enumがインクリメンタルで数値に関連付けられている場合は、次のようにenum値にマップする数値の範囲を使用できます。

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

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

これは、文字列や数値以外の何かに関連付けられた列挙型ではうまく機能しませんが、そうであればうまくいきます!


0

allValuesを作成する列挙型の拡張。

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

このコードはOPの問題の解決策を提供する可能性がありますが、このコードが質問に回答する理由や方法に関する追加のコンテキストを提供することを強くお勧めします。同様の問題を経験している将来の視聴者がソリューションの背後にある理由を理解できないため、コードのみの回答は通常、長期的には役に立たなくなります。
E. Zeytinci
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.