0

I am trying to write a hashing algorithm that's gonna successfully hash System.Windows.Input.Key values with modifier key states, for instance:

ctrl = false
shift = true
alt = false
capslock = true
numlock = false
scroll lock = false
key: A

So a key value like this should be separated from others with different states for ctrl, shift, alt, etc. But since these are just true or false, I am not sure how to make it distinct for the hash values?

Any ideas? It just have to be unique enough to handle all possible key combinations.

Joan Venge
  • 315,713
  • 212
  • 479
  • 689

1 Answers1

1

I would build a class containing all values able to compute it's own hash code, like:

    class KeyInfo : IEquatable<KeyInfo>
    {
        public bool Ctrl { get; private set; }
        public bool Shift { get; private set; }
        public bool Alt { get; private set; }
        public bool CapsLock { get; private set; }
        public bool NumLock { get; private set; }
        public bool ScrollLock { get; private set; }
        public Keys Key { get; private set; }

        public KeyInfo(bool ctrl, bool shift, bool alt, bool capsLock, bool numLock, bool scrollLock, Keys key)
        {
            this.Ctrl = ctrl;
            this.Shift = shift;
            this.Alt = alt;
            this.CapsLock = capsLock;
            this.NumLock = numLock;
            this.ScrollLock = scrollLock;
            this.Key = key;
        }

        public override bool Equals(object obj)
        {
            return this.Equals(obj as KeyInfo);
        }

        public bool Equals(KeyInfo other)
        {
            if (other == null)
                return false;
            return this.Ctrl == other.Ctrl && this.Shift == other.Shift &&
                   this.Alt == other.Alt && this.CapsLock == other.CapsLock &&
                   this.NumLock == other.NumLock && this.ScrollLock == other.ScrollLock &&
                   this.Key == other.Key;
        }

        public override int GetHashCode()
        {
            unchecked
            {
                int hash = 17;
                hash = hash * 23 + this.Ctrl.GetHashCode();
                hash = hash * 23 + this.Shift.GetHashCode();
                hash = hash * 23 + this.Alt.GetHashCode();
                hash = hash * 23 + this.CapsLock.GetHashCode();
                hash = hash * 23 + this.NumLock.GetHashCode();
                hash = hash * 23 + this.ScrollLock.GetHashCode();
                hash = hash * 23 + this.Key.GetHashCode();
                return hash;
            }
        }
    }

Credits to this Jon Skeet's answer for the GetHashCode() implementation.

N.B.

this class can be effectively used as a Dictionary key, into HashSet or in LINQ Distinct() and other LINQ sets' operations.

EDIT:

I'd like to enforce the fact that you mustn't use the hash code as dictionary key, but use the whole class instead.
You cannot rely on the uniqueness of the hash-codes because hashing it's subjected to collisions.

Community
  • 1
  • 1
digEmAll
  • 56,430
  • 9
  • 115
  • 140
  • Thanks digEmAll, that looks like a solid implementation. Do you know if bools have a specific GetHashCode implementation by itself? – Joan Venge Feb 03 '11 at 18:30
  • 1
    Yes, it returns 0 if false and 1 if true. – digEmAll Feb 03 '11 at 19:18
  • BTW, have a look to my edit (maybe is useless but just to be sure...) ;) – digEmAll Feb 03 '11 at 19:28
  • Thanks, but I didn't get the first part of your edit. You mean I should create a class for the key data? Because I already have one for it, like you did. – Joan Venge Feb 03 '11 at 19:29
  • Also why did you use unchecked scope for the hash? – Joan Venge Feb 03 '11 at 19:31
  • @Joan: about your first question, it's probably something that you already know, I wrote it just to be clearer (and it didn't work :P). I meant that, for example, you mustn't use the hashcode as key of dictionaries, but the KeyInfo class itself. – digEmAll Feb 03 '11 at 19:56
  • @Joan: about the second question, `unckecked` allows hash variable to overflow without any exception. – digEmAll Feb 03 '11 at 19:57
  • Thanks, yeah I know the first one. But for the overflow stuff, I know that but was wondering if it overflows, will it stay like that? Wouldn't that leave an invalid int value? Or is it guaranteed to be scaled back? – Joan Venge Feb 03 '11 at 20:39
  • @Joan: about the overflow... when one implements a `GetHashCode()` function there's one fundamental thing to pay attenction to i.e.: **"Return always the same number for equal instances"**. So it doesn't matter if it returns 233456476457 or 3 (overflowed), it's important to return always the same number starting from the same input values (in this case shift, alt, capslock...). – digEmAll Feb 03 '11 at 21:19
  • 2
    @Joan: If it overflows, it will not "scale back", but wrap around. For example, `int.MaxValue + 1 == int.MinValue`, and `int.MaxValue + 1000 == int.MinValue + 999`. – Justin Feb 03 '11 at 21:24
  • Thanks guys, I didn't realize it was wrapping around. – Joan Venge Feb 03 '11 at 21:41