Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you enumerate OptionSetType in Swift?

I have a custom OptionSetType struct in Swift. How can I enumerate all values of an instance?

This is my OptionSetType:

struct WeekdaySet: OptionSetType {     let rawValue: UInt8      init(rawValue: UInt8) {         self.rawValue = rawValue     }      static let Sunday        = WeekdaySet(rawValue: 1 << 0)     static let Monday        = WeekdaySet(rawValue: 1 << 1)     static let Tuesday       = WeekdaySet(rawValue: 1 << 2)     static let Wednesday     = WeekdaySet(rawValue: 1 << 3)     static let Thursday      = WeekdaySet(rawValue: 1 << 4)     static let Friday        = WeekdaySet(rawValue: 1 << 5)     static let Saturday      = WeekdaySet(rawValue: 1 << 6) } 

I would like to something like this:

let weekdays: WeekdaySet = [.Monday, .Tuesday] for weekday in weekdays {     // Do something with weekday } 
like image 630
Florian Avatar asked Aug 19 '15 18:08

Florian


People also ask

What is OptionSet in Swift?

An OptionSet in Swift is a very lightweight object that can be used to represent a fairly large number of boolean values. While you can initialize it with an array literal, it's actually much more like a Set than an array.

What is OptionSet?

A type that presents a mathematical set interface to a bit set.


2 Answers

As of Swift 4, there are no methods in the standard library to enumerate the elements of an OptionSetType (Swift 2) resp. OptionSet (Swift 3, 4).

Here is a possible implementation which simply checks each bit of the underlying raw value, and for each bit which is set, the corresponding element is returned. The "overflow multiplication" &* 2 is used as left-shift because << is only defined for the concrete integer types, but not for the IntegerType protocol.

Swift 2.2:

public extension OptionSetType where RawValue : IntegerType {      func elements() -> AnySequence<Self> {         var remainingBits = self.rawValue         var bitMask: RawValue = 1         return AnySequence {             return AnyGenerator {                 while remainingBits != 0 {                     defer { bitMask = bitMask &* 2 }                     if remainingBits & bitMask != 0 {                         remainingBits = remainingBits & ~bitMask                         return Self(rawValue: bitMask)                     }                 }                 return nil             }         }     } } 

Example usage:

let weekdays: WeekdaySet = [.Monday, .Tuesday] for weekday in weekdays.elements() {     print(weekday) }  // Output: // WeekdaySet(rawValue: 2) // WeekdaySet(rawValue: 4) 

Swift 3:

public extension OptionSet where RawValue : Integer {      func elements() -> AnySequence<Self> {         var remainingBits = rawValue         var bitMask: RawValue = 1         return AnySequence {             return AnyIterator {                 while remainingBits != 0 {                     defer { bitMask = bitMask &* 2 }                     if remainingBits & bitMask != 0 {                         remainingBits = remainingBits & ~bitMask                         return Self(rawValue: bitMask)                     }                 }                 return nil             }         }     } } 

Swift 4:

public extension OptionSet where RawValue: FixedWidthInteger {      func elements() -> AnySequence<Self> {         var remainingBits = rawValue         var bitMask: RawValue = 1         return AnySequence {             return AnyIterator {                 while remainingBits != 0 {                     defer { bitMask = bitMask &* 2 }                     if remainingBits & bitMask != 0 {                         remainingBits = remainingBits & ~bitMask                         return Self(rawValue: bitMask)                     }                 }                 return nil             }         }     } } 
like image 120
Martin R Avatar answered Oct 13 '22 02:10

Martin R


Based on the previous answers I created a generic Swift 4 solution with IteratorProtocol:

public struct OptionSetIterator<Element: OptionSet>: IteratorProtocol where Element.RawValue == Int {     private let value: Element      public init(element: Element) {         self.value = element     }      private lazy var remainingBits = value.rawValue     private var bitMask = 1      public mutating func next() -> Element? {         while remainingBits != 0 {             defer { bitMask = bitMask &* 2 }             if remainingBits & bitMask != 0 {                 remainingBits = remainingBits & ~bitMask                 return Element(rawValue: bitMask)             }         }         return nil     } } 

Then in OptionSet extension implement makeIterator()

assuming your OptionSets will be Int:

extension OptionSet where Self.RawValue == Int {    public func makeIterator() -> OptionSetIterator<Self> {       return OptionSetIterator(element: self)    } } 

Right now every time you create an OptionSet, just conform it to Sequence.

struct WeekdaySet: OptionSet, Sequence {     let rawValue: Int      ... } 

You should now be able to iterate over it:

let weekdays: WeekdaySet = [.monday, .tuesday] for weekday in weekdays {     // Do something with weekday } 

I'd also create a typealias to be explicit on what is used:

typealias SequenceOptionSet = OptionSet & Sequence

like image 33
milo Avatar answered Oct 13 '22 04:10

milo