Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Selecting a random value from a subsection of an enum

Tags:

enums

swift

Given a Swift enum as such:

enum PerformerPosition: Int {

    case String_Violin1
    case String_Violin2
    case String_Viola
    case String_Cello
    case String_CB

    case Wind_Oboe
    case Wind_Clarinet
    case Wind_Flute
    ...

}

(For the needs of the project, I am not able to have a nested enum.) I would like to randomly select an enum value with the String_ prefix, only.

The only way I know so far is to do a random enum value from all the available cases, as such:

private static let _count: PerformerPosition.RawValue = {
    // find the maximum enum value
    var maxValue: Int = 0
    while let _ = PerformerPosition(rawValue: maxValue) { 
        maxValue += 1
    }
    return maxValue
}()

static func randomPerformer() -> PerformerPosition {
    // pick and return a new value
    let rand = arc4random_uniform(UInt32(count))
    return PlayerPosition(rawValue: Int(rand))!
}

How could I make it so I am able to pick a random value based on the String_ prefix only without having to resort to hard-coding an upper value (for example, new String_ prefix positions may be added)? Thanks

like image 641
daspianist Avatar asked Mar 31 '17 16:03

daspianist


1 Answers

So you don't want to change any of the code even if a new position is added. Right?

To do that, you need to get all the enum cases dynamically, instead of hardcoding them. According to this answer, you can use this method to get all the cases:

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

After you got this cases method, you can easily get what you want by:

let startingWithString = Array(PerformerPosition.cases().filter { "\($0)".hasPrefix("String_") })
let rand = arc4random_uniform(UInt32(startingWithString.count))
let randomPosition = startingWithString[Int(rand)]
like image 115
Sweeper Avatar answered Sep 20 '22 18:09

Sweeper