9

I'd like to have an enum with two cases with a Hashable associated type each conform to Hashable, like so:

enum WordOrNumber {
    case Word(String)
    case Number(Int)
}

The first idea I had for hashing was the following:

extension WordOrNumber: Hashable {
    var hashValue: Int {
        switch self {
            case let .Word(word): 
                return word.hashValue & ~1 // ends in 0
            case let .Number(number):
                return number.hashValue | 1 // ends in 1
        }
    }
}

What I'm not sure about, is how this will interact with buckets in Swift's Dictionary and Set implementations.

Is it be better to differentiate the two cases by the LSB or MSB, or by a bit somewhere in the middle?

I assume it doesn't make any difference to shift the hashValues left by one and then adding 1, or not, but I would be interested to know if this isn't the case.

EDIT: I just tested this, and it looks like the hashValue of an Int is the value itself. This is obviously a problem, since you often get ints in succession, like in an enum Bla: Int {}. So my new and improved(?) hashValue is:

extension WordOrNumber: Hashable {
    var hashValue: Int {
        switch self {
            case let .Word(word): // ends in 0
                return word.hashValue << 1
            case let .Number(number): // ends in 1
                return number.hashValue << 1 &+ 1
        }
    }
}

The above question about LSB and MSB still stands.

  • There is actually no need to differentiate those two cases. What do you want to achieve by that? – Sulthan May 16 '16 at 13:34
  • I'm not sure what you mean? Surely there might be a conflict in the hashValues of the String and the Int where .Number(x) == .Word(y), which is problematic? – Sasha Lopoukhine May 16 '16 at 13:47
  • Why should that be problematic? Hash functions are not meant to return unique results, what's really important is the definition of equality (`Equatable`, function `==`). I would also suppose that both `Set` and `Dictionary` use a secondary hashing function. – Sulthan May 16 '16 at 14:19
  • From Swift docs: "Set and dictionary performance depends on hash values that minimize collisions for their associated element and key types, respectively." So it is a question of how frequent collisions between the associated data hashes might be. – Step Jan 18 '17 at 14:58

1 Answers1

6

Having something like:

extension WordOrNumber: Hashable {
    var hashValue: Int {
        switch self {
        case .Word(let value):
            return value.hashValue
        case .Number(let value):
            return value.hashValue
        }
    }

    static func ==(lhs: WordOrNumber, rhs: WordOrNumber) -> Bool {
        switch (lhs, rhs) {
        case (.Word(let lhsValue), .Word(let rhsValue)):
            return lhsValue == rhsValue
        case (.Number(let lhsValue), .Number(let rhsValue)):
            return lhsValue == rhsValue
        default:
            return false
        }
    }
}

... should be enough.

eMdOS
  • 1,693
  • 18
  • 23