Make Struct Hashable

Make struct Hashable?

Simply return dbName.hashValue from your hashValue function. FYI - the hash value does not need to be unique. The requirement is that two objects that equate equal must also have the same hash value.

struct PetInfo: Hashable {
var petName: String
var dbName: String

var hashValue: Int {
return dbName.hashValue
}

static func == (lhs: PetInfo, rhs: PetInfo) -> Bool {
return lhs.dbName == rhs.dbName && lhs.petName == rhs.petName
}
}

Swift 5.+ - Making a class hashable?

Here is an example for using class-based model in the described use-case. Tested with Xcode 11.4

class Stuff: Hashable, Equatable {
static func == (lhs: Stuff, rhs: Stuff) -> Bool {
lhs.title == rhs.title
}

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

var title: String = ""
}

struct StaffView: View {
let listOfStaff: [Stuff]

var body: some View {
ScrollView {
ForEach(listOfStaff, id: \.self) { stuff in
Text(stuff.title)
}
}
}
}

Hashable struct with interchangeable properties?

This is how Hasher works

https://developer.apple.com/documentation/swift/hasher

However, the underlying hash algorithm is designed to exhibit
avalanche effects: slight changes to the seed or the input byte
sequence will typically produce drastic changes in the generated hash
value.

So the problem here in hash(into:) func

Since the sequence matters combine operation is not commutative. You should find some other function to be a hash for this struct. In your case the best option is

    func hash(into hasher: inout Hasher) {
hasher.combine(leftOperand & rightOperand)
}

As @Martin R pointed out to have less collisions it's better to use ^

    func hash(into hasher: inout Hasher) {
hasher.combine(leftOperand ^ rightOperand)
}

Struct hash for different types

You can make a protocol containing all the shared data, so both S1 and S2 go through the same hasher.

I created a SharedData protocol containing the shared properties (only data in this case) and gave it a Hashable implementation. Then S1 and S2 now conform to SharedData. It looks like this:

protocol SharedData: Hashable {
var data: String { get }
}

extension SharedData {
func hash(into hasher: inout Hasher) {
hasher.combine(data)
}
}

struct S1: SharedData {
let data: String
let otherData: String

// ... Equality & Hash functions, both only use data
}
struct S2: SharedData {
let data: String
let otherData: Int

// ... Equality & Hash functions, both only use data
}

And the comparison looks like this:

print(S1(data: "hi", otherData: "there").hashValue)
print(S2(data: "hi", otherData: 1).hashValue)

let set: Set<Int> = [S1(data: "Hello", otherData: "world!").hashValue, S1(data: "abc", otherData: "123").hashValue]

let obj2 = S2(data: "Hello", otherData: 0)
print(set.contains(obj2.hashValue))

Prints:

-5068166682035457964  // <- these change every program execution
-5068166682035457964
true

Unfortunately it's not possible to do the following, yet:

let set: Set<SharedData> = [S1(data: "Hello", otherData: "world!"), S1(data: "abc", otherData: "123")]

Error:

Protocol 'SharedData' as a type cannot conform to 'Hashable'

I believe SE-0309 (Unlock existentials for all protocols) could fix this.

Conforming to Hashable protocol?

You're missing the declaration:

struct DateStruct: Hashable {

And your == function is wrong. You should compare the three properties.

static func == (lhs: DateStruct, rhs: DateStruct) -> Bool {
return lhs.year == rhs.year && lhs.month == rhs.month && lhs.day == rhs.day
}

It's possible for two different values to have the same hash value.

Swift: Hashable struct with dictionary property

I think you need to review your data model if you have to use a whole struct as a dictionary key. Anyhow, here's one way to do it:

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
}

This assumes that you don't have semicolon (;) in id or in the keys and values of values. Hasable implies Equatable so you don't need to declare it conforming to Equatable again.

How to natively hash a struct without randomization?

You should make your own protocol, something like:

protocol Signable {
/// A signature that's stable across app launches
var signature: Signature { get }
}

Equatable/Hashable are standard protocols with very specific semantics (Equality implies substitutability, and Hashability is a heuristic, as a performance optimization of Equatable). They're not catch-all bags of syntax for anything vaguely similar to equality checking or hashing.

Also beware: the randomly seeded behavior of Hasher is intentional. It protects you against hash-flooding DoS attacks. You should be very careful circumventing this, because you'll be exposing yourself to this vector.

Using signature to compare instances persisted across app launches seems fine. But I would not recommend using that to implement any kind of hashed data structures, which could be attacked to degenerate into linear-time-lookups.



Related Topics



Leave a reply



Submit