C# - Tuple or other multikey variant for Dictionar

2019-06-01 21:12发布

问题:

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.

回答1:

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]);


回答2:

You will need to define your own class with two elements and implement IComparable where CompareTo will be written as per you requirement. Then use that class as Key for your Dictionary. Also, please override method GetHashCode too.



回答3:

Is comparing List<string> a viable option for your use case? If so, I'd recommend this stack overflow anser:

Compare two List<T> objects for equality, ignoring order

If you can't use List<string>, then I'd agree with rbaghbanli's answer.