Double Key Dictionary

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!

12th of November edit: Code has finally been moved to Github! Please contribute!

18 thoughts on “Double Key Dictionary

  1. 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? :)

  2. 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? :)

  3. 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.

  4. 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. :)

  5. Your solution was complex unnecesarily. you could have used the following:

    Dictionary d;

    d.Add(new(KeyValuePair(“key1, “key2″)), “value”);

    It’s simple. The reason is the key is a pair.

  6. Edgarin: Yes, I know. I say so in the post:
    “you could do is something like this:
    Dictionary<string, Dictionary> bookList”

    But I don’t like that API very much and it makes it hard to do stuff like foreach and contains. So I made a wrapper class to handle that for me.

    1. Not sure what you mean by “two keys for same object”; would that be something like this:
      list[key1] == object1
      list[key2] == object1

      Not sure how this could be accomplished… never had the need for it I guess. Please post here if you find a solution.

  7. Hi there! This post couldn’t be written any better! Reading through this post reminds me of my old room mate! He always kept chatting about this. I will forward this article to him. Fairly certain he will have a good read. Thanks for sharing!

  8. Thank you, I have just been searching for info about this topic for ages and yours is the best I’ve discovered till now. But, what about the conclusion? Are you sure about the source?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>