可选键字典

3

有没有什么方法可以创建一个带有Optional键的字典?我知道这个问题的妥善解决需要实现条件遵从,但在那之前我有其他选择吗?

let dict: [Int?: Int] = [nil: 1]
// ERROR: type 'Int?' does not conform to protocol 'Hashable'

你确定你想要这个吗?根据定义,nil键表示没有值,而且该类型无法转换为NSDictionary,因为Foundation类型仅支持非可选类型作为键值。 - vadian
@vadian 是的,我不需要将 NSDictionary 用于鸟类。这是我的使用情况:我正在构建一个类层次结构,包括 Objective C 运行时中的各个类。这个字典将把类映射到它们的子类数组上。根类具有一个“nil”超类,通过 class_getSuperclass 给出。 - Alexander
@vadian 它可以与 NSDictionary 进行桥接,因为 nil 被桥接为 NSNull - Hamish
1个回答

4

更新:Swift 4.2

条件一致性已在Swift 4.2中实现,棒极了!

这允许当包装的元素是Hashable时,可以有条件地使Optional成为Hashable。因此,您可以直接将任何Optional<T: Hashable>用作字典键!

let d: [Int?: String] = [
    nil: "nil",
    1: "a",
    2: "b",
    3: "c",
]

for (key, value) in d {
    let stringKey = key.map(String.init(describing:)) ?? "nil"
    print("\(stringKey): \(value)")
}

在 Swift 4.2 之前

这里有一个我找到的解决方法:创建一个新的HashableOptional 枚举

enum HashableOptional<Wrapped: Hashable> {
    case none
    case some(Wrapped)

    public init(_ some: Wrapped) {
        self = .some(some)
    }

    public init(_ optional: Wrapped?) {
        self = optional.map{ .some($0) } ?? .none
    }

    public var value: Wrapped? {
        switch self {
            case .none: return nil
            case .some(let wrapped): return wrapped
        }
    }
}

extension HashableOptional: Equatable {
    static func ==(lhs: HashableOptional, rhs: HashableOptional) -> Bool {
        switch (lhs, rhs) {
            case (.none, .none): return true
            case (.some(let a), .some(let b)): return a == b
            default: return false
        }
    }   
}

extension HashableOptional: Hashable {
    var hashValue: Int {
        switch self {
            case .none: return 0
            case .some(let wrapped): return wrapped.hashValue
        } 
    }
}

extension HashableOptional: ExpressibleByNilLiteral {
    public init(nilLiteral: ()) {
        self = .none
    }
}

然后你可以像这样使用它:

let dict: [HashableOptional<Int>: Int] = [nil: 1]

个人而言,我会将包装类型设置为具有公开的 Wrapped? base 属性的 "struct",然后仅在需要访问可选值时引用 key.base。因为Optional带有很多内置的语言特性,在包装器本身上无法复制(可选链式调用、可选绑定、隐式提升、语法糖模式等),所以在包装器和底层值之间做明确区分可以提供更一致的接口。 - Hamish
@Hamish 不错的观点。你看,我没有添加map和flatmap。我觉得直接访问值会更容易,而且我也可以直接存储它。你知道的,我有点喜欢在任何类型上使用条件绑定,无论是none还是some情况。 - Alexander

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接