I am writting a set of hashValue functions to make it easier for classes/structs to conform to Hashable and to provide consistency and reliability of implementation. I wanted to cover a lot of use cases and the best I came up with is:
// MARK: Hash functions (by iterating `hash = 31 &* hash &+ h.hashValue` for each `h`)
// /// Returns: An integer that is relatively unique and therefore suitable for hash tables (dictionaries)./
// /// Hash value for nil or empty `= 1`.
// static func hashValue(nilLiteral: () = nil) -> Int {
// return 1
// }
/// Returns: An integer that is relatively unique and therefore suitable for hash tables (dictionaries).
/// Hash value for a hashable `= 31 &+ h.hashValue`.
static func hashValue1Hashable<T: Hashable>(h: T) -> Int {
return 31 &+ h.hashValue
}
/// Returns: An integer that is relatively unique and therefore suitable for hash tables (dictionaries).
/// Hash value for two hashables `= 31 &* Function.hashValueHashables(h1) &+ h2.hashValue`.
static func hashValue2Hashables<T1: Hashable, T2: Hashable>(h1: T1, _ h2: T2) -> Int {
return 31 &* Function.hashValueHashables(h1) &+ h2.hashValue
}
/// Returns: An integer that is relatively unique and therefore suitable for hash tables (dictionaries).
/// Hash value for an optional array of hashables, the given array defaults to nil wich has a hash value of 1.
/// Start with hash = 1 and then for each `h` do `hash = 31 &* hash &+ h.hashValue`.
/// If `hs` is empty or nil then resturns 1.
static func hashValueHashables<T: Hashable>(hs: [T]? = nil) -> Int {
guard let hs = hs else {
return 1
}
var hash = 1
for h in hs { hash = 31 &* hash &+ h.hashValue }
return hash
}
/// Returns: An integer that is relatively unique and therefore suitable for hash tables (dictionaries).
/// Hash value for an optional array of optional hashables.
/// Start with hash = 1 and then for each `h` do `hash = 31 &* hash &+ h.hashValue`.
/// If `hs` is empty or nil then resturn 1.
/// If any `h` is nil then it is taken as having a hash value of 1.
static func hashValueHashables<T: Hashable>(hs: [T?]?) -> Int {
guard let hs = hs else {
return 1
}
var hash = 1
for h in hs { hash = 31 &* hash &+ (h == nil ? 1 : h!.hashValue) }
return hash
}
/// Returns: An integer that is relatively unique and therefore suitable for hash tables (dictionaries).
/// Hash value for a 'var arg' array of hashables.
/// Start with hash = 1 and then for each `h` do `hash = 31 &* hash &+ h.hashValue`.
static func hashValueHashables<T: Hashable>(hs: T...) -> Int {
var hash = 1
for h in hs { hash = 31 &* hash &+ h.hashValue }
return hash
}
/// Returns: An integer that is relatively unique and therefore suitable for hash tables (dictionaries).
/// Hash value for a 'var arg' array of optional hashables.
/// Start with hash = 1 and then for each `h` do `hash = 31 &* hash &+ h.hashValue`.
/// If any `h` is nil then it is taken as having a hash value of 1.
static func hashValueHashables<T: Hashable>(hs: T?...) -> Int {
var hash = 1
for h in hs { hash = 31 &* hash &+ (h == nil ? 1 : h!.hashValue) }
return hash
}
Whilst the above works there are a number of things I don't like:
- I can't think of a way to write `hashValue` so that `hashValue()` and `hashValu(nil)` both return 1. My attempt is commented out at the top of the listing. Not that the function isn't generic since empty and nil both return 1 regardless of type. I have also tried approximated the function by giving `hashValueHashables<T: Hashable>(hs: [T]? = nil)` a default of `nil`, as shown, but that doesn't work either.
- Duplicate functions are needed to cover optional and non-optional variations, leading to potential cut and paste errors.
- Duplicate functions are needed to cover array and var arg variations, leading to potential cut and paste errors.
- The common specializations `hashValue1Hashable<T: Hashable>(h: T)` and `hashValue2Hashables<T1: Hashable, T2: Hashable>(h1: T1, _ h2: T2)` require unique names which means that they have to be manually selected (making their use unlikely in practice).
- You can't write an `arrayLiteral` version, which means that `hashValueHashables([1, 2, 3])` creates a temporary array which is disguarded. (I am assuming that `arrayLiteral` is more efficient, though it is yet another overload.)
Is there a better way of coding this?