iSofia
iSofia

Reputation: 1532

Copying ENUM hash and raw values into a dictionary

Is there any way to copy all the values of an enumeration into a dictionary without polling them in any FOR loop?

For example, from this enumeration:

enum FruitPriority: String {
    case PEARS
    case ORANGES
    case APPLES
}

to call some single function from within the ENUM, something like this:

var fruitPriorityArray: [String : Int] = FruitPriority.someFunction()

and get this result (sorted according to hash value):

["PEARS": 0, "ORANGES": 1, "APPLES": 2]

The preference would be to make only a single call to the ENUM.

Thank you.

Upvotes: 0

Views: 801

Answers (3)

Vyacheslav
Vyacheslav

Reputation: 27221

enum FruitPriority: String, CaseIterable {
    case PEARS
    case ORANGES
    case APPLES
}

let result = FruitPriority.allCases.enumerated().reduce([String: Int]()) { dict, fruit in
    var dict = dict
    dict[fruit.element.rawValue] = fruit.offset
    return dict
}

print(result)

The result is:

["PEARS": 0, "ORANGES": 1, "APPLES": 2]

For version Swift 4.1 and earlier the implementation of CaseIterable:

#if swift(>=4.2)
#else
public protocol CaseIterable {
    associatedtype AllCases: Collection where AllCases.Element == Self
    static var allCases: AllCases { get }
}
extension CaseIterable where Self: Hashable {
    static var allCases: [Self] {
        return [Self](AnySequence { () -> AnyIterator<Self> in
            var raw = 0
            return AnyIterator {
                let current = withUnsafeBytes(of: &raw) { $0.load(as: Self.self) }
                guard current.hashValue == raw else {
                    return nil
                }
                raw += 1
                return current
            }
        })
    }
}
#endif

Original post of CaseIterable implementation.

Upvotes: 2

vacawama
vacawama

Reputation: 154631

If you make your enum CaseIterable, you can construct a dictionary using reduce(into:). Since the hashValue can now change from run to run, I would recommend using enumerated() to number the cases in order:

enum FruitPriority: String, CaseIterable {
    case PEARS
    case ORANGES
    case APPLES
}

let result = FruitPriority.allCases.enumerated().reduce(into: [:]) { $0[$1.element.rawValue] = $1.offset }

print(result)

// ["ORANGES": 1, "APPLES": 2, "PEARS": 0]

Upvotes: 1

Robert Dresler
Robert Dresler

Reputation: 11200

If you need just single call to the enum:

Add CaseIterable to your enum and then just create function getDictionary in enum which returns you dictionary (for each enum case will be rawValue assigned as key and hashValue as value )

enum FruitPriority: String, CaseIterable {

    case PEARS
    case ORANGES
    case APPLES

    func getDictionary() -> [String: Int] {
        var dictionary = [String: Int]()
        FruitPriority.allCases.forEach {
            dictionary[$0.rawValue] = $0.hashValue
        }
        return dictionary
    }

}

then you can just call

var fruitPriorityArray: [String : Int] = FruitPriority.getDictionary()

Note: if you're using earlier versions of Swift you can see this to create CaseIterable protocol

Upvotes: 1

Related Questions