วิธีรับค่า enum ทั้งหมดเป็นอาร์เรย์


104

ฉันมี enum ดังต่อไปนี้

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

ฉันเพิ่มวิธีนี้ลงใน enum

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 ฉันต้องการเพิ่มฟังก์ชันนี้ให้กับ enum เอง ดังนั้นฉันไม่ต้องไปเพิ่มทุกที่ในที่อื่น ๆ ของ codebases ถ้าฉันเพิ่มค่าอื่นให้กับ enum
Isuru

อาจซ้ำกันได้ของHow to enumerate an enum with String type?
mmmmmm

ฉันมีคำตอบที่คุณสามารถอ้างถึงได้ที่นี่stackoverflow.com/a/48960126/5372480
MSimic

คำตอบ:


156

สำหรับ 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.2

ไม่คุณไม่สามารถค้นหาenumค่าที่มีอยู่ได้ ดูบทความนี้ คุณต้องกำหนดอาร์เรย์ที่แสดงรายการค่าทั้งหมดที่คุณมี ตรวจสอบโซลูชันของ Frank Valbuena ใน " วิธีรับค่า enum ทั้งหมดเป็นอาร์เรย์ "

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

ดูคำตอบนี้: stackoverflow.com/a/28341290/8047รวมถึงรหัส Swift 3
Dan Rosenstark

3
การอัปเกรดสำหรับส่วน allValues ​​แต่ไม่แน่ใจว่าจะรู้สึกอย่างไรเกี่ยวกับสตริงที่เป็น enum แต่เริ่มต้นด้วย Int
Tyress

ลิงค์แรกเสีย แต่ดูเหมือนจะอยู่ที่exceptionshub.com/…ตอนนี้
The Tin Man

40

Swift 4.2เปิดตัวโปรโตคอลใหม่ชื่อCaseIterable

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

เมื่อคุณปฏิบัติตามคุณจะได้รับอาร์เรย์จากenumกรณีเช่นนี้

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

28

เพิ่มโปรโตคอลCaseIterableให้กับ enum:

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

สังเกตว่าสิ่งนี้ใช้ได้กับ enum ประเภทใด ๆ (RawRepresentable หรือไม่) และหากคุณเพิ่มกรณีใหม่คุณจะได้รับข้อผิดพลาดของคอมไพเลอร์ซึ่งเป็นสิ่งที่ดีเนื่องจากจะบังคับให้คุณอัปเดตสิ่งนี้


1
นอกรีต แต่ได้ผลและเตือนคุณหากคุณแก้ไขกรณี enum วิธีแก้ที่ชาญฉลาด!
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


ดูเหมือนจะเป็นทางออกที่ดี แต่มีข้อผิดพลาดในการคอมไพล์ค่อนข้างน้อยใน Swift 4
Isuru

1
"บางแห่ง" อาจเป็น: theswiftdev.com/2017/10/12/swift-enum-all-values (และอื่น ๆ ?) สินเชื่อ blogger CoreKit
AmitaiB

5
สิ่งนี้แตกใน XCode 10 (โดยไม่คำนึงถึง Swift-version) เนื่องจาก hashValue ของ enum ไม่เพิ่มขึ้นอีกต่อไป แต่เป็นการสุ่มทำลายกลไก วิธีใหม่ในการทำเช่นนี้คือการอัปเกรดเป็น Swift 4.2 และใช้ CaseIterable
Yasper

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

  static var statusList: [String] {
    return EstimateItemStatus.allCases.map { $0.rawValue }
  }
}

["รอดำเนินการ", "OnHold", "เสร็จสิ้น"]


8

ในการรับรายการเพื่อวัตถุประสงค์ในการใช้งานให้ใช้นิพจน์EnumName.allCasesที่ส่งคืนอาร์เรย์เช่น

EnumName.allCases.map{$0.rawValue} 

จะให้รายการสตริงที่ระบุ EnumName: String, CaseIterable

หมายเหตุ: ใช้allCasesแทนAllCases().


2

อัปเดตสำหรับ Swift 5

ทางออกที่ง่ายที่สุดที่ฉันพบคือใช้.allCasesกับ enum ที่ขยายออกไป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ในCaseIterableenum ใด ๆจะส่งคืนCollectionองค์ประกอบนั้น

var myEnumArray = EstimateItemStatus.allCases

ข้อมูลเพิ่มเติมเกี่ยวกับCaseIterable


ไม่จำเป็นต้องใช้ description () เพียงแค่นำแต่ละกรณีมาเปรียบเทียบกับสตริงเช่นcase OnHold = "On Hold"และนั่นจะกลายเป็นค่าดิบสำหรับแต่ละกรณี
pnizzle

@pnizzle ฉันรู้ว่ามันอยู่ที่นั่นเพราะมันอยู่ในคำถามเดิม
Christopher Larsen

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

1

หลังจากแรงบันดาลใจจากลำดับและชั่วโมงของข้อผิดพลาดลอง n ในที่สุดฉันก็ได้ Swift 4 ที่สะดวกสบายและสวยงามบน 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
    }
}

การใช้งาน:

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
ข้อบกพร่องหนึ่งรหัสที่นี่แม้ว่า มันพลาดรายการแรกในเคส เปลี่ยน 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) }

สิ่งนี้ใช้ไม่ได้กับ enums ที่เกี่ยวข้องกับสตริงหรือสิ่งอื่นใดนอกจากตัวเลข แต่จะใช้งานได้ดีหากเป็นเช่นนั้น!


0

ส่วนขยายบน enum เพื่อสร้าง 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.