Cách lấy tất cả các giá trị enum dưới dạng một mảng


101

Tôi có enum sau đây.

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

Tôi cần lấy tất cả các giá trị thô dưới dạng một mảng chuỗi (giống như vậy ["Pending", "On Hold", "Done"]).

Tôi đã thêm phương thức này vào enum.

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

Nhưng tôi gặp lỗi sau.

Không thể tìm thấy trình khởi tạo cho kiểu 'GeneratorOf' chấp nhận danh sách đối số kiểu '(() -> _)'

Có cách nào dễ dàng hơn, tốt hơn hoặc thanh lịch hơn để làm điều này không?


2
bạn có thể tạo mảng giống như mảng let: [Ước tínhItemStatus] = [.Pending, .Onhold, .Done]
Kristijan Delivuk

1
@KristijanDelivuk Tôi muốn thêm chức năng này vào chính enum. Vì vậy, tôi không cần phải đi và thêm nó ở mọi nơi ở những nơi khác của cơ sở mã nếu tôi thêm một giá trị khác vào enum.
Isuru


Tôi có một câu trả lời bạn có thể tham khảo tại đây stackoverflow.com/a/48960126/5372480
MSimic

Câu trả lời:


149

Đối với Swift 4.2 (Xcode 10) trở lên

Có một CaseIterablegiao thức:

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

Đối với Swift <4,2

Không, bạn không thể truy vấn một enumgiá trị mà nó chứa. Xem bài viết này . Bạn phải xác định một mảng liệt kê tất cả các giá trị bạn có. Ngoài ra, hãy xem giải pháp của Frank Valbuena trong " Cách lấy tất cả các giá trị enum dưới dạng một mảng ".

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

Xem câu trả lời này: stackoverflow.com/a/28341290/8047 bao gồm mã Swift 3.
Dan Rosenstark

3
Ủng hộ cho phần allValues, nhưng không chắc phải cảm nhận gì về một enum là kiểu String nhưng được khởi tạo bằng Int.
Tyress

Liên kết đầu tiên bị hỏng nhưng dường như hiện đang ở exceptionshub.com/… .
The Tin Man

39

Swift 4.2 giới thiệu một giao thức mới có tênCaseIterable

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

mà khi bạn tuân theo, bạn có thể nhận được một mảng từ các enumtrường hợp như thế này

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

27

Thêm giao thức CaseIterable vào enum:

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

Sử dụng:

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

17

Có một cách khác ít nhất là an toàn tại thời điểm biên dịch:

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

Lưu ý rằng điều này hoạt động đối với bất kỳ loại enum nào (RawRepresentable hoặc không) và cũng như nếu bạn thêm một trường hợp mới thì bạn sẽ gặp lỗi trình biên dịch, điều này tốt vì sẽ buộc bạn phải cập nhật điều này.


1
Không chính thống, nhưng nó hoạt động và nó cảnh báo bạn nếu bạn sửa đổi các trường hợp enum. Giải pháp thông minh!
Chuck Krutsinger

12

Tôi tìm thấy ở đâu đó mã này:

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

Sử dụng:

enum YourEnum: EnumCollection { //code }

YourEnum.cases()

trả về danh sách các trường hợp từ YourEnum


Có vẻ như một giải pháp tuyệt vời nhưng có một vài lỗi biên dịch trên Swift 4.
Isuru

1
"Ở đâu đó" có thể là: theswiftdev.com/2017/10/12/swift-enum-all-values (trong số những người khác?). Blogger ghi có CoreKit .
AmitaiB

5
Điều này bị phá vỡ trong XCode 10 (bất kể phiên bản Swift) vì hashValue của enum không còn gia tăng mà là ngẫu nhiên, phá vỡ cơ chế. Cách mới để làm điều này là để nâng cấp lên Swift 4.2 và sử dụng CaseIterable
Yasper

8

Để nhận danh sách cho các mục đích chức năng, hãy sử dụng biểu thức EnumName.allCasestrả về một mảng, ví dụ:

EnumName.allCases.map{$0.rawValue} 

sẽ cung cấp cho bạn một danh sách các Chuỗi được cung cấp EnumName: String, CaseIterable

Lưu ý: sử dụng allCasesthay vì 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 }
  }
}

["Đang chờ xử lý", "OnHold", "Done"]


2

Cập nhật cho Swift 5

Giải pháp dễ nhất mà tôi đã tìm thấy là sử dụng .allCasestrên một enum mở rộngCaseIterable

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

.allCasestrên CaseIterableenum nào sẽ trả về một Collectionphần tử đó.

var myEnumArray = EstimateItemStatus.allCases

thêm thông tin về CaseIterable


Không cần triển khai mô tả (). Chỉ cần cân bằng mỗi trường hợp với chuỗi ví dụ case OnHold = "On Hold"và điều đó trở thành giá trị thô cho mỗi trường hợp.
pnizzle

@pnizzle Tôi biết, nó ở đó vì nó nằm trong câu hỏi ban đầu.
Christopher Larsen

1

Đối với 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))
}

Để dùng nó:

arrayEnum(MyEnumClass.self)

Tại sao các phần tử hashValuesẽ là 0..n?
NRitH

1

Sau cảm hứng từ Trình tự và hàng giờ thử n lỗi. Cuối cùng tôi đã có được Swift 4 cách thoải mái và đẹp mắt này trên 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
    }
}

Sử dụng:

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

Đầu ra:

Pending
On Hold
Done

1

Bạn có thể dùng

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

Đẹp! Sau khi nâng cấp từ Swift 3.2 lên 4.1, đây là một giải pháp mà tôi đã sử dụng. Ban đầu chúng tôi có khai báo AnyItertor <Self>. Giải pháp của bạn sạch hơn và dễ đọc hơn nhiều. cảm ơn!
Nick N

2
Tuy nhiên, một lỗ hổng mã ở đây. Nó bỏ lỡ mục đầu tiên trong trường hợp. Thay đổi var index = 1 thành var index = 0
Nick N

0

Nếu enum của bạn tăng dần và được liên kết với các số, bạn có thể sử dụng dải số mà bạn ánh xạ đến các giá trị enum, như sau:

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

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

Điều này không hoàn toàn hoạt động với enum được liên kết với chuỗi hoặc bất kỳ thứ gì khác ngoài số, nhưng nó hoạt động tốt nếu đúng như vậy!


0

Mở rộng trên một enum để tạo allValues.

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

Mặc dù mã này có thể cung cấp giải pháp cho vấn đề của OP, nhưng bạn rất nên cung cấp ngữ cảnh bổ sung về lý do và / hoặc cách mã này trả lời câu hỏi. Các câu trả lời chỉ có mã thường trở nên vô dụng về lâu dài vì người xem trong tương lai gặp các vấn đề tương tự không thể hiểu lý do đằng sau giải pháp.
E. Zeytinci
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.