Чи можна в Swift перетворити рядок на перерахування?


93

Якщо у мене є перелік із випадками a, b, c, d, чи можна мені відкинути рядок "a" як перерахування?


3
Ці "зливки" називаються буквальними перетвореннями.
Vatsal Manot

Відповіді:


136

Звичайно. Перерахування можуть мати вихідну вартість. Щоб процитувати документи:

Сировинними значеннями можуть бути рядки, символи або будь-який з цілих чи типів чисел із плаваючою комою

- Витяг від: Apple Inc. “Швидка мова програмування”. 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)\"")

Примітка: Вам не потрібно писати = "один" тощо після кожного випадку. Значення рядка за замовчуванням такі самі, як імена регістрів, тому виклик .rawValueпросто поверне рядок

РЕДАГУВАТИ

Якщо вам потрібно, щоб значення рядка містило такі речі, як пробіли, які не є дійсними як частина значення справи, тоді вам потрібно явно встановити рядок. Так,

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

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

дає

anEnum = "один"

Але якщо ви хочете case oneвідобразити "значення одне", вам потрібно буде вказати рядкові значення:

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

Вихідне значення має бути буквально конвертованим. Ви не можете використовувати будь-який Hashableтип.
Vatsal Manot

1
Гаразд ... Я процитував документи Apple, де перераховані типи значень, які можна використовувати як перелічені значення enum. Рядки, питання OP, є одним із підтримуваних типів.
Duncan C,

1
Хм, уявіть case one = "uno". Тепер, як проаналізувати "one"значення перерахування? (не можна використовувати сирі, як вони використовуються для локалізації)
Agent_L

Можливо, ви можете ініціалізувати необроблений рядок під час ініціалізації залежно від локалізації ... або просто мати різний перелік кожного для іншої локалізації. У будь-якому випадку ціллю створення переліку є абстрагування базового сировинного тексту, тобто локалізації. Хороший дизайн коду ніде не передавав би параметр "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)

Технічно це не правильна відповідь, оскільки це перевіряє вихідну вартість. У наведеному нижче прикладі не вказано вихідне значення, тому воно неявно відповідає імені справи, але якщо у вас є перелік із необробленим значенням, це порушується.
Mark A. Donohoe

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

3
Це єдина відповідь, яка насправді працює за запитом OP, що стосується імен регістрів, а не вихідних значень. Хороша відповідь!
Mark A. Donohoe

1
Хоча це працює, це дуже дурне. Будь ласка, не базуйте функціональність на назвах справ у коді.
Султан

7
Що ще він повинен робити? Що робити, якщо він пише перелік у базу даних, а потім потрібно повернути його назад?
Джо

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

1
@Peterdk, будь ласка, підкажіть найкращу можливу альтернативу. Рішення Ігоря насправді просто працювало для мене.
Hemang

@Hemang Це працює нормально, добре, але кращим рішенням буде підтримка Swift для автоматичного здійснення цього. Це божевільно робити це вручну для кожного переліку. Але так, це працює.
Peterdk

@Peterdk, ти можеш додати окрему відповідь на те саме? Це напевно допомогло б усім тут.
Hemang,

1
Це не божевільно, що Свіфт не підтримує його рідно. Божевільна річ полягає в тому, що функціонал покладається на назву типу. Коли значення зміниться, вам доведеться переробляти та перейменовувати всі звичаї. Це не правильний спосіб вирішити це.
Султан

2

Розширення відповіді Дункана С.

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

Свіфт 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 та їх представлення String я оголошую 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)")
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.