Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Map OptionSetType to Array

Tags:

swift

Given the following:

struct Weekdays: OptionSetType {

    let rawValue: Int
    init(rawValue: Int) { self.rawValue = rawValue }

    static let Monday = Weekdays(rawValue: 1)
    static let Tuesday = Weekdays(rawValue: 2)
    static let Wednesday = Weekdays(rawValue: 4)
    static let Thursday = Weekdays(rawValue: 8)

    static let allOptions: [Weekdays] = [.Monday, .Tuesday, .Wednesday, .Thursday]

}

I can convert an array of Ints into a Weekdays object by doing this:

let arr = [1, 4]
let weekdays = arr.reduce(Weekdays()) { $0.union(Weekdays(rawValue: $1)) }

My question is, how do I take a Weekdays object and convert it into an array of Ints?

like image 892
Daniel T. Avatar asked Oct 18 '25 18:10

Daniel T.


2 Answers

(Not necessarily better, but a different way to look at it and slightly more general).

OptionSetType inherits from RawRepresentable and therefore can be converted from and to the associated raw type, which in your case is Int.

So the "missing link" is a conversion between the raw value (e.g. 5) and an integer array of the bitwise components (e.g. [1, 4]).

This can be done with an Int extension method:

extension Int {
    init(bitComponents : [Int]) {
        self = bitComponents.reduce(0, combine: (+))
    }

    func bitComponents() -> [Int] {
        return (0 ..< 8*sizeof(Int)).map( { 1 << $0 }).filter( { self & $0 != 0 } )
    }
}

Then your conversion from an array to a Weekdays object becomes

let arr : [Int] = [1, 4]
let weekdays = Weekdays(rawValue: Int(bitComponents: arr))
print(weekdays)
// app.Weekdays(rawValue: 5)

and the reverse conversion

let array = weekdays.rawValue.bitComponents()
print(array)
// [1, 4]

Advantages:

  • The explicit definition of allOptions: is not needed.
  • It can be applied to other option set types (which have Int as a raw value).

One could also try to define the conversions as a protocol extension, e.g. of IntegerType, so that the same works with other integer raw types as well. However, this seems to be a bit complicated/ugly because the left shift operator << is not part of the IntegerType (or any) protocol.


Update for Swift 3:

extension Int {
    init(bitComponents : [Int]) {
        self = bitComponents.reduce(0, +)
    }

    func bitComponents() -> [Int] {
        return (0 ..< 8*MemoryLayout<Int>.size).map( { 1 << $0 }).filter( { self & $0 != 0 } )
    }
}
like image 75
Martin R Avatar answered Oct 20 '25 09:10

Martin R


Not exactly answering the question, but might be useful to others. Based on Martin's answer I extract back the component objects:

extension FixedWidthInteger {
    init(bitComponents : [Self]) {
        self = bitComponents.reduce(0, +)
    }

    var bitComponents : [Self] {
        (0 ..< Self.bitWidth).map { 1 << $0 } .filter { self & $0 != 0 }
    }
}

extension OptionSet where RawValue: FixedWidthInteger, Self == Self.Element {
    var components : [Self] { rawValue.bitComponents.map(Self.init) }
}
like image 27
Ivorius Avatar answered Oct 20 '25 09:10

Ivorius