Double Key Dictionary, or; how to make it really simple to use a dictionary with two keys.
In a matter of just weeks I found myself forced to temporarily store values with two unique identifying keys. As an example think about a customer in a web shop who has bought some books. Key number one would be the customer ID and then key number two would be the books ISBN number. The value stored could be the books name. Why you would want to do this I have no idea, but it’s an easy to understand example and it illustrates the problem I had so that everybody can understand it. Its just that I read values from, literally, thousands of Xml logs stored in a database… Anyway!
So what you could do is something like this:
Dictionary<string, Dictionary<string, string>> bookList = new Dictionary<string, Dictionary<string, string>>();
Dictionary<string, string> innerList = new…
innerList.Add(“11-22-33″, “Lord of the Rings”);
bookList.Add(“James”, innerList);
That works and would perhaps be good enough. But what happens if you want to compare two lists of books? And it’s not unthinkable that you might end up trying to add the same book, for the same person, more than once. How do you handle that? Lots of manual checks? More on my solution later on…
Even if I could make the problems outlined above go away (as in create little helper methods to sort them out) I just didn’t like the syntax. It felt just so wrong. What I wanted would be something like this:
DoubleKeyDictionary<string, string, string> bookListEx = new DoubleKeyDictionary<string, string, string>();
bookListEx.Add(“James”, “11-22-33″, “Lord of …”);
(Abbreviated for clarity)
So I implemented the DoubleKeyDictionary, as a two key generic dictionary. My implementation also includes IEnumerable<T>, IEquatable<T> and I added an index. All this enables me to use this syntax:
DoubleKeyDictionary<string, string, string> bookListEx = new DoubleKeyDictionary<string, string, string>();
bookListEx.Add(“James”, “11-22-33″, “Lord of the Rings”);
DoubleKeyDictionary<string, string, string> bookList = new DoubleKeyDictionary<string, string, string>();
bookList.Add(“James”, “10-20-30″, “Narnia”);
if (bookList.Equals(bookListEx))
Console.WriteLine(“They are equal”);
foreach (DoubleKeyPairValue<string, string, string> books in bookListEx)
Console.WriteLine(books.ToString());
Sorry about the bad formatting. Please download the actual code file [new window]. Please feel free to use the code if you like it. If not; tell me why!

February 21, 2008 at 20:56 |
Hey, thats an informative piece.
Was just wondering about the namespace that needs to be included ?
February 22, 2008 at 9:14 |
Not sure what you mean by that, but you’ll need System.Collections.Generic for sure. And the namespace in the code is just Utility I believe, feel free to change it.
Does that answer your question?
July 30, 2008 at 14:27 |
A nice, clear, piece of code.
The problem is, what if I only have one key, not both?
Thanks.
July 30, 2008 at 14:39 |
What do you mean? I’m guessing what you mean is this;
You have a DoubleKey Dictionary with this content:
Key1 – Key2 – Value1
Key1 – Key3 – Value2
Key4 – Key5 – Value3
And then you want to extract all values for Key1. Is that the scenario you’re referring to? If so; I guess you’d have to build your own index’er… I haven’t looked at the code for this for some time now, but I guess it would look a little like this:
public Dictionary this[K index] {
get {
return OuterDictionary[index];
}
set {
OuterDictionary[index] = value;
}
}
Please note that I haven’t tested this, as I’m at work right now and are unable to verify it. But this code, if it works, should return a dictionary of (T, V). If you only wanted to return the values you’d have to do some more work, change the signature to “public List index[K index]. And the “setter” wouldn’t be possible then I guess…
Makes sense? Or did I screw up royally now?
September 12, 2008 at 22:25 |
Hello Noocyte,
Nice post, I was just starting to code the same generic class..
greets, Tom
December 4, 2008 at 23:14 |
I needed to add ContainsKey to your code:
public bool ContainsKey(K key1, T key2)
{
bool bReturn = false;
if (OuterDictionary.ContainsKey(key1))
if (m_innerDictionary.ContainsKey(key2))
bReturn = true;
return bReturn;
}
This led to the discovery that the following:
dictX.Add(“aa”, “bb”, 1);
dictX.Add(“cc”, “dd”, 2);
gave the wrong result of dictX.ContainsKey(“aa”, “dd”) is TRUE.
which led to the discovery that:
dictX.Add(“aa”, “bb”, 1);
dictX.Add(“cc”, “dd”, 2);
dictX.Add(“cc”, “bb”, 3);
gave the incorrect result of dictX(“aa”, “bb”) => 3
so, I had to make m_innerDictionary a local inside Add & change ContainsKey, which resulted in the elimination of the private m_innerDictionary & the following code changes (which now work for me):
public void Add(K key1, T key2, V value)
{
if (OuterDictionary.ContainsKey(key1))
{
if (OuterDictionary[key1].ContainsKey(key2))
OuterDictionary[key1][key2] = value;
else
{
Dictionary m_innerDictionary = OuterDictionary[key1];
m_innerDictionary.Add(key2, value);
OuterDictionary[key1] = m_innerDictionary;
}
}
else
{
Dictionary m_innerDictionary = new Dictionary();
m_innerDictionary[key2] = value;
OuterDictionary.Add(key1, m_innerDictionary);
}
}
public bool ContainsKey(K key1, T key2)
{
bool bReturn = false;
if (OuterDictionary.ContainsKey(key1))
if (OuterDictionary[key1].ContainsKey(key2))
bReturn = true;
return bReturn;
}
Thanks for the class. Hope this helps.
December 5, 2008 at 8:35 |
Thanx for commenting Scott! I’m thinking of updating the code and adding unit tests to it, that way I can reveal stuff like this. Should’ve done that when I wrote the code, I know, but this really was just a quick attempt at writing some generic code.
Oh, and please don’t use Hungarian notation in .Net code!
bool containsKey is better, IMO.