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
Apple's Description of Reference and Value Types with Multiple Threads
Xcode Beta 6 "Type of Expression Is Ambiguous Without More Context" Navigationlink
Convert Character to Integer in Swift
Swift 4.2 Setter Getter, All Paths Through This Function Will Call Itself
Swift String Interpolation Displaying Optional
How to Test If Objects Conforming to the Same Protocol Are Identical in Swift Without Casting
Parse.Com Querying User Class (Swift)
Why I Can Change/Reassigned a Constant Value That Instantiated from a Class
Unexpectedly Large Realm File Size
Why Can't I Use a Tuple Constant as a Case in a Switch Statement
Swift 3: Convert a Null-Terminated Unsafepointer<Uint8> to a String
Swift 5.5: Asynchronously Iterating Line-By-Line Through a File
Swiftui Customized Text Field in Osx
Invalid Update: Invalid Number of Items in Section 0
How to Send Data from iPhone to Watchkit in Swift
Select Filtered Search Results