Swift: 具有字典属性的可哈希结构体

6

我在Swift中有一个结构体,它看起来像这样:

internal struct MapKey {
    internal let id: String
    internal let values: [String:String]
}
extension MapKey: Equatable {}
func ==(lhs: MapKey, rhs: MapKey) -> Bool {
    return lhs.id == rhs.id && lhs.values == rhs.values
}

我现在需要在Swift字典中使用MapKey作为键,这要求MapKey符合Hashable协议。

对于像这样的结构体,Hashable的正确实现是什么?

extension MapKey: Hashable {
    var hashValue: Int {
        return ??? // values does not have a hash function/property.
    }
}

我一直在做研究,但是没有找到对字典进行哈希的正确方法,因为我需要能够为 values 属性本身生成哈希值。非常感谢任何帮助。

3个回答

4
我认为如果您必须使用整个结构体作为字典键,那么您需要重新审查您的数据模型。无论如何,以下是一种方法:

我认为如果您必须使用整个结构体作为字典键,那么您需要重新审查您的数据模型。无论如何,以下是一种方法:

internal struct MapKey: Hashable {
    internal let id: String
    internal let values: [String:String]

    var hashValue: Int {
        get {
            var hashString = self.id + ";"
            for key in values.keys.sort() {
                hashString += key + ";" + values[key]!
            }

            return hashString.hashValue
        }
    }
}

func ==(lhs: MapKey, rhs: MapKey) -> Bool {
    return lhs.id == rhs.id && lhs.values == rhs.values
}

假设您的ID或值中没有分号(;),values的键和值中也没有。 Hasable意味着Equatable,因此您不需要再声明其符合Equatable

2
您无需对分号做任何假设(也不需要插入这样的分隔符),因为哈希值不一定是唯一的。 - Martin R
如果我错了,请纠正我。阅读这个答案,我认为你需要再次单独实现Equatable。 - mfaani
@Honey,你不需要声明符合Equatable协议,因为Hashable已经隐含了它。==函数提供了Equatable所需的实现。 - Code Different
@CodeDifferent 这不是链接答案告诉你应该做的吗?我的意思是,如果你的哈希值恰好相同,那么它将切换到可比较函数并尝试执行。还是这与另一件事有关?请解释一下? - mfaani
我终于解决了它,并在这里写下了我的答案(https://dev59.com/U1wZ5IYBdhLWcg3wQ-bk#40685353)。希望对那些有同样困惑的人有所帮助。 - mfaani
显示剩余2条评论

1

由于id和values都是不可变的,因此两者都可以用作相等性和哈希值的基础。然而,如果MapKey.id(名称有些暗示)在一个字典的上下文中唯一标识MapKey,则仅使用MakKey.id作为==运算符和哈希值的基础更加简单和高效。

    internal struct MapKey: Hashable {
        internal let id: String
        internal let values: [String:String]

        var hashValue: Int {
            get { return  self.id.hashValue}
        }
    }

    func ==(lhs: MapKey, rhs: MapKey) -> Bool {
        return lhs.id == rhs.id
    }

良好的观察,埃里克。不幸的是,在我的情况下,id + values的组合使Key唯一,尽管id会让你想其他情况。我可能没有选择最好的示例名称。 - unbekant

0

在Swift 4.2中,基础数据类型都是可哈希的,你只需要让你的MapKey结构体符合Hashable协议:

struct MapKey: Hashable {
    let id: String
    let values: [String: String]
}

如果您想使用一个类,您需要遵循哈希(:)函数的规范,如下所示:

class MapKey: Hashable {
    static func == (lhs: MapKey, rhs: MapKey) -> Bool {
        return lhs.id == rhs.id && lhs.values == rhs.values
    }

    let id: String = ""
    let values: [String: String] = [:]

    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
        hasher.combine(values)
    }
}

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