ใน Swift สามารถแปลงสตริงเป็น enum ได้หรือไม่?


97

ถ้าฉันมี enum กับกรณี a, b, c, d เป็นไปได้ไหมที่ฉันจะร่ายสตริง "a" เป็น enum?


3
'การร่าย' เหล่านี้เรียกว่าการแปลงตามตัวอักษร
Vatsal Manot

คำตอบ:


141

แน่นอน Enums สามารถมีค่าดิบ ในการอ้างอิงเอกสาร:

ค่าดิบอาจเป็นสตริงอักขระหรือประเภทตัวเลขจำนวนเต็มหรือทศนิยมใด ๆ ก็ได้

- ข้อความที่ตัดตอนมาจาก: 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 = "หนึ่ง"

แต่ถ้าคุณต้องการcase oneแสดง "ค่าหนึ่ง" คุณจะต้องระบุค่าสตริง:

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

ค่าดิบต้องแปลงได้ตามตัวอักษร คุณไม่สามารถใช้เพียงHashableประเภทใดก็ได้
Vatsal Manot

1
โอเค ... ฉันอ้างถึงเอกสารของ Apple ซึ่งแสดงรายการประเภทของค่าที่สามารถใช้เป็นค่าดิบได้ สตริงคำถามของ OP เป็นหนึ่งในประเภทที่รองรับ
Duncan C

1
case one = "uno"อืมคิด ทีนี้จะแยกวิเคราะห์"one"เป็นค่า enum ได้อย่างไร? (ใช้ดิบไม่ได้เนื่องจากใช้สำหรับการแปลภาษา)
Agent_L

บางทีคุณอาจเริ่มต้นสตริงดิบเมื่อเริ่มต้นขึ้นอยู่กับการแปล ... หรือเพียงแค่มี enum ที่แตกต่างกันสำหรับการแปลที่แตกต่างกัน ไม่ว่าในกรณีใดจุดประสงค์ทั้งหมดของการมี enum คือการแยกส่วนดิบที่เป็นพื้นฐานออกไปนั่นคือการแปล การออกแบบที่ดีรหัสจะไม่ผ่าน "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)

นี่ไม่ใช่คำตอบที่ถูกต้องในทางเทคนิคเนื่องจากจะตรวจสอบค่าดิบ ในตัวอย่างที่ระบุไม่มีการระบุค่าดิบดังนั้นจึงจับคู่กับชื่อเคสโดยปริยาย แต่ถ้าคุณมี enum ที่มีค่าดิบตัวแบ่งนี้
Mark A. Donohoe

30

ใน Swift 4.2 โปรโตคอล CaseIterable สามารถใช้กับ enum ที่มี rawValues ​​ได้ แต่สตริงควรตรงกับเลเบลเคส enum:

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
แม้ว่าจะได้ผล แต่ก็เป็นเรื่องโง่มากที่ต้องทำ โปรดอย่าใช้งานตามชื่อของกรณีและปัญหาในรหัส
Sulthan

7
เขาควรจะทำอะไรอีก? จะเกิดอะไรขึ้นถ้าเขาเขียน enum ลงในฐานข้อมูลแล้วต้องส่งกลับ?
โจ

16

ในกรณีที่ enum มีประเภท 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
} 

2
มันบ้ามากที่คุณไม่สามารถใช้ฟังก์ชันที่คล้ายกันที่มีอยู่ในภาษาได้ ฉันนึกภาพออกว่าคุณจัดเก็บค่าใน JSON ด้วยชื่อ enum จากนั้นในการแยกวิเคราะห์จำเป็นต้องแปลงกลับ การเขียนenumFromStringวิธีการสำหรับแต่ละ enum ที่คุณใช้ดูเหมือนจะบ้า
Peterdk

1
@Peterdk โปรดแนะนำทางเลือกที่ดีที่สุด วิธีแก้ปัญหาของ Igor ใช้งานได้จริงสำหรับฉัน
Hemang

@Hemang มันใช้งานได้โอเค แต่ทางออกที่ดีกว่าคือการสนับสนุน Swift สำหรับการทำสิ่งนี้โดยอัตโนมัติ มันบ้ามากที่จะทำสิ่งนี้ด้วยตนเองสำหรับแต่ละ enum แต่ใช่มันได้ผล
Peterdk

@Peterdk คุณช่วยเพิ่มคำตอบแยกกันได้ไหม แน่นอนมันจะช่วยทุกคนที่นี่
มัง

1
ไม่ใช่เรื่องบ้าที่ Swift ไม่สนับสนุนโดยกำเนิด สิ่งที่บ้าคือการทำงานขึ้นอยู่กับชื่อของประเภท เมื่อค่าเปลี่ยนไปคุณจะต้อง refactor และเปลี่ยนชื่อการใช้งานทั้งหมด นี่ไม่ใช่วิธีที่ถูกต้องในการแก้ปัญหานี้
สุลต่าน

2

ขยายคำตอบของ Duncan 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
    }
}

2

สำหรับ 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)")

1

ใช้คำตอบของ djruss70 เพื่อสร้างโซลูชันที่มีลักษณะทั่วไปสูง:

extension CaseIterable {
    static func from(string: String) -> Self? {
        return Self.allCases.first { string == "\($0)" }
    }
    func toString() -> String { "\(self)" }
}

การใช้งาน:

enum Chassis: CaseIterable {
    case pieridae, oovidae
}

let chassis: Chassis = Chassis.from(string: "oovidae")!
let string: String = chassis.toString()

หมายเหตุ: สิ่งนี้จะใช้ไม่ได้หาก enum ประกาศ @objc เท่าที่ฉันรู้เกี่ยวกับ Swift 5.3 ไม่มีวิธีใดที่จะทำให้สิ่งนี้ทำงานกับ @objc enum ยกเว้นโซลูชัน brute force (คำสั่งสวิตช์)

หากมีคนรู้วิธีทำให้งานนี้กับ @objc enums ฉันจะสนใจคำตอบนี้มาก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.