How to get all enum values as an array

IosArraysSwiftEnums

Ios Problem Overview


I have the following 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
        }
    }
}

I need to get all the raw values as an array of strings (like so ["Pending", "On Hold", "Done"]).

I added this method to the enum.

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

But I'm getting the following error.

> Cannot find an initializer for type 'GeneratorOf' that accepts an argument list of type '(() -> _)'

Is there is an easier, better or more elegant way to do this?

Ios Solutions


Solution 1 - Ios

For Swift 4.2 (Xcode 10) and later

There's a CaseIterable protocol:

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

For Swift < 4.2

No, you can't query an enum for what values it contains. See this article. You have to define an array that list all the values you have. Also check out Frank Valbuena's solution in "https://stackoverflow.com/questions/32952248/how-to-get-all-enum-values-as-an-array/46853256#46853256";.

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

Solution 2 - Ios

Swift 5

Add CaseIterable protocol to enum:

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

Usage:

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

Solution 3 - Ios

Swift 4.2 introduces a new protocol named CaseIterable

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

that when you conforms to , you can get an array from the enum cases like this

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

Solution 4 - Ios

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

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

["Pending", "OnHold", "Done"]

Solution 5 - Ios

There's another way that at least is safe at compile time:

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

Notice that this works for any enum type (RawRepresentable or not) and also if you add a new case then you will get a compiler error which is good since will force you to have this up to date.

Solution 6 - Ios

To get a list for functional purposes, use the expression EnumName.allCases which returns an array e.g.

EnumName.allCases.map{$0.rawValue} 

will give you a list of Strings given that EnumName: String, CaseIterable

Note: use allCases instead of AllCases().

Solution 7 - Ios

I found somewhere this code:

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

Use:

enum YourEnum: EnumCollection { //code }

YourEnum.cases()

return list of cases from YourEnum

Solution 8 - Ios

Update for Swift 5

Easiest solution I've found is to use .allCases on an enum that extends 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 on any CaseIterable enum will return a Collection of that element.

var myEnumArray = EstimateItemStatus.allCases

more info about CaseIterable

Solution 9 - Ios

You Can Use

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

Solution 10 - Ios

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

To use it:

arrayEnum(MyEnumClass.self)

Solution 11 - Ios

After inspiration from Sequence and hours of try n errors. I finally got this comfortable and beautiful Swift 4 way on 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
    }
}

Usage:

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

Output:

Pending
On Hold
Done

Solution 12 - Ios

If your enum is incremental and associated with numbers, you can use range of numbers that you map to enum values, like so:

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

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

This doesn't quite work with enums associated with strings or anything other than numbers, but it works great if that is the case!

Solution 13 - Ios

Extension on an enum to create allValues.

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionIsuruView Question on Stackoverflow
Solution 1 - IosCode DifferentView Answer on Stackoverflow
Solution 2 - IosmaxwellView Answer on Stackoverflow
Solution 3 - IosSh_KhanView Answer on Stackoverflow
Solution 4 - IosVladimir PchelyakovView Answer on Stackoverflow
Solution 5 - IosFrank ValbuenaView Answer on Stackoverflow
Solution 6 - IosArchy Will He 何魏奇View Answer on Stackoverflow
Solution 7 - IosDaniel KutaView Answer on Stackoverflow
Solution 8 - IosChristopher LarsenView Answer on Stackoverflow
Solution 9 - IosCarlos ChaguendoView Answer on Stackoverflow
Solution 10 - IosJean-Nicolas DefosséView Answer on Stackoverflow
Solution 11 - IosmclamView Answer on Stackoverflow
Solution 12 - IosBen PatchView Answer on Stackoverflow
Solution 13 - IosgargView Answer on Stackoverflow