Noocyte’s Weblog

February 18, 2008

Double Key Dictionary

Filed under: Personal — Tags: , — noocyte @ 23:16

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!

About these ads

13 Comments »

  1. Hey, thats an informative piece.
    Was just wondering about the namespace that needs to be included ?

    Comment by Veena Shivanna — February 21, 2008 @ 20:56

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

    Comment by noocyte — February 22, 2008 @ 9:14

  3. A nice, clear, piece of code.
    The problem is, what if I only have one key, not both?

    Thanks.

    Comment by Schmuli Raskin — July 30, 2008 @ 14:27

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

    Comment by noocyte — July 30, 2008 @ 14:39

  5. Hello Noocyte,
    Nice post, I was just starting to code the same generic class..
    greets, Tom

    Comment by Tom Akkermans — September 12, 2008 @ 22:25

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

    Comment by Scott Duncan — December 4, 2008 @ 23:14

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

    Comment by noocyte — December 5, 2008 @ 8:35

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

    Comment by Edgarin — December 16, 2009 @ 21:26

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

    Comment by noocyte — December 17, 2009 @ 7:15

  10. very nice but Im looking for something like 2 types of keys for same object not pair of keys

    Comment by Xmen — August 1, 2011 @ 13:41

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

      Comment by noocyte — October 11, 2011 @ 10:20


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

The Shocking Blue Green Theme. Create a free website or blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: