Como comparar tipos personalizados utilizando o protocol Hashable
O que é um protocol Hashable?
Um tipo que fornece um valor inteiro.
Você pode usar qualquer tipo que esteja em conformidade com o protocolo Hashable em um conjunto ou como uma chave de dicionário. Muitos tipos na biblioteca padrão estão em conformidade com Hashable: strings, integers, floats e valores booleanos, e mesmo conjuntos, fornecem um valor hash por padrão. Seus próprios tipos personalizados podem ser hashable também. Quando você define uma enumeração sem valores associados, ganha conformidade Hashable automaticamente e você pode adicionar conformidade Hashable aos seus outros tipos personalizados, adicionando uma única propriedade hashValue.
Um valor de hash, fornecido pelo tipo propriedade hashValue, é um inteiro que é o mesmo para quaisquer das duas instâncias que se comparam igualmente. Ou seja, para duas instâncias a e b do mesmo tipo, se a == b então a.hashValue == b.hashValue. O inverso não é verdade, duas instâncias com valores de hash iguais não são necessariamente iguais entre si. — Apple Developer
Na prática
Entendendo sobre isso, vamos criar uma situação para ver em ação o protocol Hashable. Vamos definir um Array de Strings:
let a = [“four”,”one”, “two”, “one”, “three”,”four”, “four”]
Vamos implementar uma função que irá realizar um filtro e mostrar apenas os registros únicos.
func unique<S: Sequence, E: Hashable>(_ source: S) -> [E] where E==S.Iterator.Element {
var seen: [E:Bool] = [:]
return source.filter {
seen.updateValue(true, forKey: $0) == nil
}
}
A função acima recebe uma sequencia qualquer desde que os elementos da sequencia estejam em conformidade com o protocol Hashable, e então utilizamos um filtro para retornar os valores únicos.
unique(a) // ["four", "one", "two", "three"]
Achou fácil? E se fosse um tipo personalizado, como ficaria? Neste caso precisamos implementar o protocol Hashable e isso não tem segredo, vamos ver na prática criando uma struct Person.
struct Person {
var name: String
var age: Int
}
Para usar seu próprio tipo personalizado em um conjunto ou como o tipo de chave de um dicionário, adicione conformidade Hashable ao seu tipo fornecendo uma propriedade hashValue. O protocolo Hashable herda do protocolo Equatable, então você também deve adicionar uma função de operador igual (==) para seu tipo personalizado.
extension Person: Hashable {
var hashValue: Int {
get {
return name.hashValue ^ age.hashValue
}
} static func ==(l: Person, r: Person) -> Bool {
return l.hashValue == r.hashValue
}
}
A propriedade hashValue neste exemplo combina os valores hash dos valores name e age de uma Person usando o operador XOR bit a bit (^). O operador ^ é uma maneira de combinar dois valores inteiros em um único valor.
let p = [Person(name: "Jack", age: 22),
Person(name: "Anne", age: 26),
Person(name: "Mary", age: 27),
Person(name: "Anne", age: 26)]unique(p) // ["Jack", "Anne", "Mary"]
E agora conseguimos comparar, filtrar e realizar diversas operações com tipos personalizados de uma maneira fácil e prática.
O código esta disponível no Github :