Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dictionary supporting duplicate, multidimensional keys?

I have a List<Thing> things, where a number of Things need to be frequently retrieved by looking up a combination of two variables T1 f1 and T2 f2, which are value types. They way I do that now is simply things.Where(t => t.Field1 == f1 && t.Field2 == f2). However, I do extremely many of those lookups frequently, and need a more effective method.

Fortunately, things does not need to have elements removed or added, so I thought of parsing the list on construction and add to a Dictionary<T1, Lookup<T2, Thing>>. However, this feels messy, especially with the added parsing. And it gets really hairy if I need to lookup even more fields. Three fields would look like Dictionary<T1, Dictionary<T2, Lookup<T3, Thing>>>.

My next thought was to make a Lookup<Tuple<T1,T2,T3,...>,Thing>. But in this case, I am not sure whether the keys will actually work because Tuple is a reference type.

Even if I make a Lookup<ValueType<T1,T2,T3,...>,Thing> things, the lookup statement will be something like things[new ValueType<T1,T2,T3,...>(f1, f2, f3, ...)] which is pretty ugly (and I am still not sure whether I could trust those keys).

Is there a more elegant solution to this which keeps the performance benefits of a hashtable and where I could simply type something like IEnumerable<Thing> found = things[f1, f2, f3, ...];?

like image 656
David S. Avatar asked Aug 24 '12 16:08

David S.


People also ask

Can you have duplicate keys in a dictionary in Python?

As you can see in the Screenshot, the output displays the dictionary but it does not have duplicate keys in a dictionary because in Python dictionary does not allow duplicate keys. If you want to get all those values from a list and store them in the dictionary then you have to use the unique key with every value.

Is there a dictionary class which permits duplicate keys?

A Dictionary Class Which Permits Duplicate Keys. The .NET framework contains a number of ‘Dictionary’ classes. The .NET framework contains a number of 'Dictionary' classes including: All of these associate a key with a value and are represented internally by a collection of key/value pairs.

How are dictionary keys and values represented in the framework?

The .NET framework contains a number of ‘Dictionary’ classes. All of these associate a key with a value and are represented internally by a collection of key/value pairs.

Can you insert duplicate entries in a dictionary class?

Well I was enlightened by the awesome post and decided to explore the Lookup class and its usage. Well in the screenshot you can see that you cannot insert duplicate entries in a Dictionary class so you do need a custom list to store those values.


2 Answers

Lookup<Tuple<T1,T2,T3,...>,Thing> will work, since Tuple overrides Equals and GetHashCode.

To make the lookup syntax less ugly, you can use Tuple.Create which supports type inference. Your code becomes things[Tuple.Create(f1, f2, f3, ...)]. If that's still too ugly, it's trivial to add a helper method that takes the individual values as parameters.

I'd also consider creating my own immutable class(or value type) for the key, so you get clean field names instead of ItemX. You just need to override Equals and GetHashCode consistently.

like image 89
CodesInChaos Avatar answered Oct 12 '22 12:10

CodesInChaos


You can create multiple lookups, and then intersect them to do your searches. Here is a somewhat oversimplified example, but it should illustrate the idea:

class Test {
    public string A { get; set; }
    public string B { get; set; }
    public string C { get; set; }
}

var list = new List<Test> {
    new Test {A = "quick", B = "brown", C = "fox"}
,   new Test {A = "jumps", B = "over", C = "the"}
,   new Test {A = "lazy", B = "dog", C = "quick"}
,   new Test {A = "brown", B = "fox", C = "jumps"}
,   new Test {A = "over", B = "the", C = "lazy"}
,   new Test {A = "dog", B = "quick", C = "brown"}
,   new Test {A = "fox", B = "jumps", C = "over"}
,   new Test {A = "the", B = "lazy", C = "dog"}
,   new Test {A = "fox", B = "brown", C = "quick"}
,   new Test {A = "the", B = "over", C = "jumps"}
,   new Test {A = "quick", B = "dog", C = "lazy"}
,   new Test {A = "jums", B = "fox", C = "brown"}
,   new Test {A = "lazy", B = "the", C = "over"}
,   new Test {A = "brown", B = "quick", C = "dog"}
,   new Test {A = "over", B = "jumps", C = "fox"}
,   new Test {A = "dog", B = "lazy", C = "the"}
};
var byA = list.ToLookup(v => v.A);
var byB = list.ToLookup(v => v.B);
var byC = list.ToLookup(v => v.C);
var all = byA["quick"].Intersect(byB["dog"]);
foreach (var test in all) {
    Console.WriteLine("{0} {1} {2}", test.A, test.B, test.C);
}
all = byA["fox"].Intersect(byC["over"]);
foreach (var test in all) {
    Console.WriteLine("{0} {1} {2}", test.A, test.B, test.C);
}

This prints

quick dog lazy
fox jumps over
like image 20
Sergey Kalinichenko Avatar answered Oct 12 '22 13:10

Sergey Kalinichenko