将OptionSetType映射到Array

时间:2015-06-28 13:22:16

标签: swift

鉴于以下内容:

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]

}

我可以通过这样做将Ints数组转换为Weekdays对象:

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

我的问题是,如何使用Weekdays对象并将其转换为Ints数组?

4 个答案:

答案 0 :(得分:7)

(不一定更好,但以不同的方式来看待它并稍微有点 更一般)。

OptionSetType继承自RawRepresentable,因此可以 从和转换为关联的原始类型,在您的情况下是 Int

所以"缺少链接"是原始值之间的转换(例如5) 和按位分量的整数数组(例如[1, 4])。

可以使用Int扩展方法完成此操作:

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

然后,您从数组转换为Weekdays对象变为

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

和反向转换

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

优点:

  • 不需要allOptions:的明确定义。
  • 它可以应用于其他选项集类型(具有Int 作为原始价值)。

还可以尝试将转换定义为协议扩展, 例如IntegerType的{​​{1}},以便同样适用于其他整数原始类型。然而,这似乎有点复杂/丑陋 因为左移操作符<<不属于 IntegerType(或任何)协议。

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

答案 1 :(得分:1)

当我写这个问题时,我想出来了:

let array = Weekdays.allOptions.filter { weekdays.contains($0) }.map { $0.rawValue }

有更好的方法吗?

答案 2 :(得分:1)

不能完全回答问题,但对其他人可能有用。根据马丁的回答,我提取出组件对象:

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

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

extension OptionSet where RawValue == Int, Self == Self.Element {
    var components : [Self] {
        return rawValue.bitComponents.map { return type(of: self).init(rawValue: $0) }
    }
}

答案 3 :(得分:1)

您可以通过在OptionSet中有条件地定义扩展名来改善其上下文。

extension OptionSet where RawValue: UnsignedInteger {
    var individualCases: [Self] {
        return (0..<(8 * MemoryLayout<RawValue>.size))
            .map { bitsToShift in RawValue(1 << bitsToShift) } // Get every possible single-bit flag
            .filter { (powerOfTwo: RawValue) -> Bool in rawValue & powerOfTwo != 0 } // filter out only the cases the receiver contains
            .map { Self(rawValue: $0) } // create the `OptionSet` (single bit) type
    }
}

let weekdays = Weekdays(rawValue: 0b11111)
weekdays.individualCases.map { $0.rawValue } // [1, 2, 4, 8, 16]

警告:在我的13英寸2019 MacBookPro上,我必须提供上述所有显式类型,才能在Swift 5.0中将方法类型检查保持在1500ms以内。

感谢MartinR启发了我们重新设计内存布局大小的方法。

为完成示例,我将以下工作日类型更新为下面的Swift 5,并明确使用UInt8类型来提高individualCases的效率。使用UInt时,它将分别循环前64个循环mapfilter,使用UInt8则仅循环8次。

struct Weekdays: OptionSet {

    let rawValue: UInt8

    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 Friday = Weekdays(rawValue: 16)
}