TOC
Collections:

Dictionaries

Dictionaries in C# all implement the IDictionary interface. There are several Dictionary types, but the most commonly used is the generic Dictionary, often referred to as Dictionary<TKey, TValue> - it holds a type-specific key and a corresponding type-specific value. This is basically what separates a Dictionary from a List - the items of a list comes in a specific order and can be accessed by a numerical index, where items in a Dictionary is stored with a unique key, which can then be used to retrieve the item again.

We'll dig more into what dictionaries are all about, but first, let's look at a simple example, to show you what it's all about:

Dictionary<string, int> users = new Dictionary<string, int>();  
users.Add("John Doe", 42);  
users.Add("Jane Doe", 38);  
users.Add("Joe Doe", 12);  
users.Add("Jenna Doe", 12);

Console.WriteLine("John Doe is " + users["John Doe"] + " years old");

Meet the Doe family, who are once again the test-data for an example in this tutorial. Dad, Mom and the twins Joe and Jenna. Notice how I define the Dictionary to use a string as the key (in this case, the name) and an integer for the value (in this case an age). The cool thing is that this allows us to access an item from the dictionary using the key (name), instead of a numerical index. You can see that in action in the last line of the example - between a set of square brackets, we simply specify the key, to access the value (the age).

There are two important things to know here: First of all, the key has to be unique. In this case, where we use a string as a key, it means that you can't add two users with the exact same name. That makes pretty good sense, because when you can use the key to reference a single item, the key can only point to one value. On the other hand, this makes this example a bit too simple, because obviously you can't have a collection of users where duplicate names can't exist, but bear with me for now.

The other thing you will have to remember, when accessing values from a dictionary, is that the key has to exist in the collection, when you try to access its value. In other words, my example was only safe because I was in full control of what the list contained - in most situations, you should always check if the key exists, before trying to use it - like this:

string key = "John Doe";
if(users.ContainsKey(key))
    Console.WriteLine("John Doe is " + users[key] + " years old");

If you try to add a key which already exists, or if you try to access a value with a key which doesn't exists, .NET will throw an exception. This might sound obvious, but both are actually very commonly found in the exception logs around the world.

Working with the items

Accessing a single item can be very useful, but what if you want to loop through the collection and e.g. look for something? In that case, the first thing you should be aware of is that items in the list are obviously not just a simple object - instead, the Dictionary hold items of the type KeyValuePair<TKey, TValue>. The T's are the types you have used to declare the list - in this case a string and an integer. So looping through the collection with a foreach loop would look something like this:

Dictionary<string, int> users = new Dictionary<string, int>()
{
    { "John Doe", 42 },
    { "Jane Doe", 38 },
    { "Joe Doe", 12 },
    { "Jenna Doe", 12 }
};
foreach (KeyValuePair<string, int> user in users)
{
    Console.WriteLine(user.Key + " is " + user.Value + " years old");
}

The first thing you might notice is that I've used the opportunity to change the way we initialize the dictionary - instead of manually adding each item after instantiating the dictionary, we use the collection initializer syntax, as described in a previous article.

With that out of the way, we now have the same dictionary as before - let's loop through it. As you can see, I use a foreach loop and I have declared the type of item I expect as a KeyValuePair<string, int> - the exact same two types used to declare the dictionary. Now we can access both the key and the value (name and age) for each item, and we use that to generate a simple string of information about the person to the Console.

The order of items

The above example brings us to a very important point: Unlike a List, where the basic order of items is determined by numerical indexes, the order of items in a dictionary is non-deterministic - you simply can't rely on the items being in a certain order, not even when you manually add each item individually. Sure, when you run the above example, you will likely experience that the items are in the exact same order as we added them, but that's not guaranteed, and as soon as you have a dictionary with more items and start adding and removing items, the order will likely change.

The Dictionary class doesn't come with a Sort() method, because in the end, even if you sort it, the order might change as soon as you start working with it again. Instead, you can use the OrderBy() and OrderByDescending() methods from LINQ (more on that in another chapter) to get a sorted copy of the Dictionary. This also allows you to sort either by the key or by the value, which could be useful in our example, to get the users out in the order of their age:

Dictionary<string, int> users = new Dictionary<string, int>()
{
    { "John Doe", 42 },
    { "Jane Doe", 38 },
    { "Joe Doe", 12 },
    { "Jenna Doe", 12 }
};
foreach (KeyValuePair<string, int> user in users.OrderBy(user => user.Value))
{
    Console.WriteLine(user.Key + " is " + user.Value + " years old");
}

If you run the example, you will see that even though the dictionary is declared with the items in one order, we can easily get them out in another order, as we see fit. Just remember that you are responsible for ensuring that you get the items out in the order you want, because the .NET framework stores them exactly as it sees fit.