Swiftで、文字列を列挙型に変換することは可能ですか?


93

ケースa、b、c、dの列挙型がある場合、文字列 "a"を列挙型としてキャストできますか?


3
これらの「キャスト」はリテラル変換と呼ばれます。
Vatsal Manot、2015年

回答:


136

承知しました。列挙型は生の値を持つことができます。ドキュメントを引用するには:

生の値は、文字列、文字、または整数または浮動小数点型のいずれかです。

—抜粋:Apple Inc.「The Swift Programming Language」iBooks。https://itun.es/us/jEUH0.l

したがって、次のようなコードを使用できます。

enum StringEnum: String 
{
  case one = "one"
  case two = "two"
  case three = "three"
}

let anEnum = StringEnum(rawValue: "one")!

print("anEnum = \"\(anEnum.rawValue)\"")

注:それぞれのケースの後に= "one"などを書く必要はありません。デフォルトの文字列値はケース名と同じであるため、呼び出し.rawValueは文字列を返すだけです

編集

文字列値に、ケース値の一部として無効なスペースなどを含める必要がある場合は、文字列を明示的に設定する必要があります。そう、

enum StringEnum: String 
{
  case one
  case two
  case three
}

let anEnum = StringEnum.one
print("anEnum = \"\(anEnum)\"")

与える

anEnum = "one"

ただし、case one「値1」を表示する場合は、文字列値を指定する必要があります。

enum StringEnum: String 
{
  case one   = "value one"
  case two   = "value two"
  case three = "value three"
}

生の値は文字通り変換可能でなければなりません。どのHashableタイプも使用できません。
Vatsal Manot、2015年

1
わかりました...列挙型の未加工の値として使用できる値のタイプをリストするAppleのドキュメントを引用しました。OPの質問である文字列は、サポートされているタイプの1つです。
ダンカンC

1
うーん、想像してみてくださいcase one = "uno"。では、"one"列挙値に解析する方法は?(ローカライズに使用されているため、rawは使用できません)
Agent_L 2016年

ローカリゼーションに応じて、初期化時に生の文字列を初期化するか、ローカリゼーションごとに異なる列挙型を使用することもできます。いずれにせよ、列挙型を持つことの全体的な目的は、基礎となる生のローカリゼーションを抽象化することです。優れたコード設計では、パラメーターとして "uno"をどこにも渡さず、StringEnum.oneに依存します
SkyWalker

5
= "one"各ケースの後に等を書く必要はありません。デフォルトの文字列値は、ケース名と同じです。
emlai

38

あなたに必要なのは:

enum Foo: String {
   case a, b, c, d
}

let a = Foo(rawValue: "a")
assert(a == Foo.a)

let 💩 = Foo(rawValue: "💩")
assert(💩 == nil)

これは生の値をチェックするため、技術的には正しい答えではありません。上記の例では、未加工の値が指定されていないため、暗黙的にケース名に一致しますが、未加工の値を持つ列挙型がある場合、これは機能しません。
マークA.ドノホー2018

26

Swift 4.2では、CaseIterableプロトコルをrawValuesを持つ列挙型に使用できますが、文字列は列挙型のケースラベルと一致する必要があります。

enum MyCode : String, CaseIterable {

    case one   = "uno"
    case two   = "dos"
    case three = "tres"

    static func withLabel(_ label: String) -> MyCode? {
        return self.allCases.first{ "\($0)" == label }
    }
}

使用法:

print(MyCode.withLabel("one")) // Optional(MyCode.one)
print(MyCode(rawValue: "uno"))  // Optional(MyCode.one)

2
これは素晴らしい答えです!それは実際に質問に対処します。
Matt Rundle、2018

3
これは、OPが要求したとおりに実際に機能する唯一の回答です。これは、生の値ではなくケース名に関するものです。いい答えだ!
マークA.ドノホー2018

1
これは機能しますが、それは非常に愚かなことです。コード内のケースの名前に基づく機能は使用しないでください。
スルタン

7
彼は他に何をすることになっていますか?データベースに列挙型を書き込んでいて、それをキャストバックする必要がある場合はどうなりますか?
Joe

15

Int型の列挙型の場合は、次のようにできます。

enum MenuItem: Int {
    case One = 0, Two, Three, Four, Five //... as much as needs

    static func enumFromString(string:String) -> MenuItem? {
        var i = 0
        while let item = MenuItem(rawValue: i) {
            if String(item) == string { return item }
            i += 1
        }
        return nil
    }
}

そして使用:

let string = "Two"
if let item = MenuItem.enumFromString(string) {
    //in this case item = 1 
    //your code
} 

1
言語に組み込まれている同様の機能を使用できないのはおかしいです。たとえば、列挙型の名前でJSONに値を格納し、解析時にそれらを元に戻す必要があると想像できます。enumFromString使用する列挙型ごとにメソッドを書くのはおかしいようです。
Peterdk 2017年

1
@Peterdk、可能な限り最良の代替案を提案してください。イゴールのソリューションは実際に私のために働いた。
Hemang 2018年

@ヘマンそれは大丈夫、大丈夫ですが、より良い解決策はこれを自動的に行うためのSwiftサポートでしょう。列挙型ごとにこれを手動で行うのはおかしいです。しかし、はい、これは機能します。
Peterdk 2018年

@Peterdk、同じことについて別の回答を追加していただけますか?それはきっとここのみんなを助けるでしょう。
Hemang 2018年

1
Swiftがネイティブでサポートしていないのはおかしくありません。奇妙なことに、機能は型の名前に依存しています。値が変更されたら、すべての使用法をリファクタリングして名前を変更する必要があります。これはこれを解決する正しい方法ではありません。
スルタン

2

ダンカンCの答えを拡張する

extension StringEnum: StringLiteralConvertible {

    init(stringLiteral value: String){
        self.init(rawValue: value)!
    }

    init(extendedGraphemeClusterLiteral value: String) {
        self.init(stringLiteral: value)
    }

    init(unicodeScalarLiteral value: String) {
        self.init(stringLiteral: value)
    }
}

2

Swift 4.2:

public enum PaymentPlatform: String, CaseIterable {
    case visa = "Visa card"
    case masterCard = "Master card"
    case cod = "Cod"

    var nameEnum: String {
        return Mirror(reflecting: self).children.first?.label ?? String(describing: self)
    }

    func byName(name: String) -> PaymentPlatform {
        return PaymentPlatform.allCases.first(where: {$0.nameEnum.elementsEqual(name)}) ?? .cod
    }
}

1

Int enumとその文字列表現の場合、enumを次のように宣言します。

enum OrderState: Int16, CustomStringConvertible {

    case waiting = 1
    case inKitchen = 2
    case ready = 3

    var description: String {
        switch self {
        case .waiting:
            return "Waiting"
        case .inKitchen:
            return "InKitchen"
        case .ready:
            return "Ready"
        }
    }

    static func initialize(stringValue: String)-> OrderState? {
        switch stringValue {
        case OrderState.waiting.description:
            return OrderState.waiting
        case OrderState.inKitchen.description:
            return OrderState.inKitchen
        case OrderState.ready.description:
            return OrderState.ready

        default:
            return nil
        }
    }
}

使用法:

order.orderState = OrderState.waiting.rawValue

let orderState = OrderState.init(rawValue: order.orderState)
let orderStateStr = orderState?.description ?? ""
print("orderStateStr = \(orderStateStr)")
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.