Search code examples
c#dictionarykeytuples

Tuple or other multikey variant for Dictionary, but with permutability


I am scratching my head over the following problem. I want to create a dictionary, which uses multiple keys. I came along the solutions suggesting tuples as the method of choice. I think this i a good way to go. However my problem has the following speciality. I would like to make the keys "permutable" (Sorry, if I am using the wrong slang here). What I mean with this, is the following. I want the result of dict[<key1,key2> to be the same as with dict[<key2,<key1>], because the data that I want to store is invariant to the order of keys.

As an example see the following code, which of course does at the moment not yield the result I am hoping for. I want the result with permuted key to be the same as for the key-tuple.

    Tuple<string, string> key = new Tuple<string, string>("Name1", "Name2");
    Dictionary<Tuple<string,string>, double> dict = new Dictionary<Tuple<string, string>, double>();
    dict.Add(key, 5.0);

    Console.WriteLine(dict[key]);

    Tuple<string, string> permutedKey = new Tuple<string, string>("Name2", "Name1");
    Console.WriteLine(dict[permutedKey]);

The reason for all this, I have to store data, which has to be indexed with two keys, but is essentially always symetrical. So there is no need, to store it two times.


Solution

  • You can define your own custom equality comparer and use it in the dictionary:

    class TupleComparer<T> : IEqualityComparer<Tuple<T, T>>
    {
        public bool Equals(Tuple<T, T> x, Tuple<T, T> y)
        {
            return object.Equals(x.Item1, y.Item1) && object.Equals(x.Item2, y.Item2) ||
                   object.Equals(x.Item1, y.Item2) && object.Equals(x.Item2, y.Item1);
        }
    
        public int GetHashCode(Tuple<T, T> obj)
        {
            return obj.Item1.GetHashCode() + obj.Item2.GetHashCode();
        }
    }
    

    Then, you can create the dictionary passing it an instance of the comparer:

    Tuple<string, string> key = new Tuple<string, string>("Name1", "Name2");
    
    Dictionary<Tuple<string,string>, double> dict = 
        new Dictionary<Tuple<string, string>, double>(new TupleComparer<string>());
    
    dict.Add(key, 5.0);
    Console.WriteLine(dict[key]);
    
    var permutedKey = new Tuple<string, string>("Name2", "Name1");
    Console.WriteLine(dict[permutedKey]);