Why check before reading
A Dictionary<TKey, TValue> looks up values by key. Reading a key that is not there is an error, not an empty answer: capitals["Spain"] throws a KeyNotFoundException with the message "The given key 'Spain' was not present in the dictionary." So when a key might be missing, ask first.
ContainsKey(key) returns true when the key is in the dictionary and false when it is not. It returns a bool, so it can be the condition of an if.
Dictionary<string, string> capitals = new Dictionary<string, string>();
capitals["France"] = "Paris";
capitals["Japan"] = "Tokyo";
if (capitals.ContainsKey("Japan")) {
Console.WriteLine(capitals["Japan"]);
}
if (!capitals.ContainsKey("Spain")) {
Console.WriteLine("No Spain");
}
Tokyo
No Spain
Inside the first if, reading capitals["Japan"] is safe, because the check has just shown that the key exists. Putting ! in front reverses the answer, so the second condition is true for a missing key. In a file of your own, add using System; and using System.Collections.Generic;.
An else branch covers the other case. Say the variable key holds "Spain":
if (capitals.ContainsKey(key)) {
Console.WriteLine(capitals[key]);
} else {
Console.WriteLine("Unknown country: " + key);
}
With the dictionary above, this prints Unknown country: Spain, and the program carries on instead of crashing.
A value is not a key
ContainsKey looks only at the keys. In the dictionary above, capitals.ContainsKey("Paris") is false, because "Paris" is a value. To look through the values, call ContainsValue("Paris"), which is true. Keys are the fast side of a dictionary. A value check has to compare against the values one at a time.
Keys must also match exactly, including upper and lower case. capitals.ContainsKey("japan") is false, since the key is "Japan".
The exercise, step by step
In pets, each key is a pet's name and each value is its kind: Rex is a dog, Tom is a cat and Polly is a parrot.
- Is Tom a key? Write an
ifonpets.ContainsKey("Tom"). Inside it, print"Tom is a "followed bypets["Tom"]. The read cannot throw here. - Is "cat" not a key? Write an
ifon!pets.ContainsKey("cat"). The word "cat" does appear inpets, but only as Tom's kind, which is a value. As a key it is missing, so the condition is true and the line is printed.
Dictionary<string, string> pets = new Dictionary<string, string>();
pets["Rex"] = "dog";
pets["Tom"] = "cat";
pets["Polly"] = "parrot";
if (pets.ContainsKey("Tom")) {
Console.WriteLine("Tom is a " + pets["Tom"]);
}
if (!pets.ContainsKey("cat")) {
Console.WriteLine("cat is not a key");
}
Tom is a cat
cat is not a key
A shortcut and some traps
- One lookup instead of two.
ContainsKeyfollowed by an index looks the key up twice.TryGetValuedoes both in one call.if (capitals.TryGetValue("Japan", out string city))is true when the key exists, and thencityholds the value. When the key is missing, it returnsfalseandcityis the default for its type, which isnullfor a string. UseContainsKeywhen you only need the yes or no. - Mixing up keys and values. Check the side you mean:
ContainsKeyfor names,ContainsValuefor kinds. - Different case.
"tom"is not"Tom". If you want keys to ignore case, create the dictionary with a comparer:new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase). ThenContainsKey("japan")finds"Japan". - Reading before checking. The whole point of the check is to keep the index read out of the missing-key case.
Key takeaways
d.ContainsKey(key)returnstrueorfalseand goes in anif.- It checks keys only.
ContainsValuechecks values. - Reading a missing key with
d[key]throws aKeyNotFoundException. TryGetValuechecks and reads in one call.
