Swift: Hashable структура со свойством словаря

У меня есть структура в 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
}

Теперь у меня есть необходимость использовать MapKey в качестве ключа в словаре Swift, который требует, чтобы MapKey соответствовал протоколу Hashable.

Какова будет правильная реализация Hashable для такой структуры, как эта?

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

Я провел некоторое исследование, но не смог определить, каков правильный способ хэширования словаря, так как мне нужно иметь возможность генерировать хеш-значение для самого свойства values. Буду признателен за любую оказанную помощь.

2 ответа

Решение

Я думаю, что вам нужно пересмотреть свою модель данных, если вам нужно использовать целую структуру в качестве словарного ключа. Во всяком случае, вот один из способов сделать это:

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 снова.

Поскольку и id, и значения являются неизменяемыми, их можно использовать как основу для equals и hashValue. Однако - если MapKey.id (что в некоторой степени подразумевает название) однозначно идентифицирует MapKey (по крайней мере, в контексте одного словаря), то проще и эффективнее просто использовать MakKey.id в качестве основы для оператора ==. как hashValue

    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
    }

Основные типы данных являются Hashable в Swift 4.2, вам нужно только позволить вашей структуре MapKey соответствовать протоколу Hashable:

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

в случае, если вы хотите использовать класс, вам нужно соответствовать hash(:) func следующим образом:

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)
    }
}
Другие вопросы по тегам