What a dictionary loop does
A foreach loop over a Dictionary visits every entry once. The loop body runs one time per key. You can read the entry's key and its value inside the body.
Dictionary<string, int> stock = new Dictionary<string, int>();
stock["apples"] = 5;
stock["pears"] = 2;
foreach (var pair in stock) {
Console.WriteLine(pair.Key + " " + pair.Value);
}
Output:
apples 5
pears 2
Each entry is a KeyValuePair<string, int>, with a Key property and a Value property. Writing var lets C# work that type out, so you don't have to type it.
Looping over the keys only
If you only need the keys, loop over stock.Keys. You can still read each value by indexing the dictionary with its key:
foreach (string fruit in stock.Keys) {
Console.WriteLine(fruit + " " + stock[fruit]);
}
This prints the same two lines. stock.Values works the same way when you only need the values, for example to add them up.
Values only, and unpacking each entry
When the keys don't matter, loop over Values. For the scores in the exercise below, this adds up the points:
int sum = 0;
foreach (int points in scores.Values) {
sum += points;
}
Console.WriteLine(sum); // 43
C# can also unpack an entry into two variables in the loop header, which reads well when you use both parts:
foreach (var (name, points) in scores) {
Console.WriteLine(name + ": " + points);
}
This prints Ana: 12, Ben: 7, Cleo: 15 and Dan: 9 on separate lines. Looping over an empty dictionary is fine: the body simply never runs, so a total stays at 0.
Order, and a trap
A Dictionary makes no promise about the order of its entries. In a small program that only adds entries, they often come out in the order you added them, but don't rely on it. Write loops whose answer doesn't depend on the order: a total, a count or a check are safe.
Don't add keys to a dictionary while you loop over it. The loop stops with an InvalidOperationException:
foreach (var pair in stock) {
stock["kiwis"] = 1; // throws: Collection was modified; enumeration operation may not execute.
}
Collect the changes first and apply them after the loop.
The exercise
The dictionary scores holds each player's points: Ana 12, Ben 7, Cleo 15 and Dan 9. The variables total and count start at 0. The task is to add every player's points to total, and to count the players with 10 points or more.
- Write a
foreachloop overscoreswithvar pair. - Inside it, add
pair.Valuetototal. - Still inside, if
pair.Valueis 10 or more, add 1 tocount.
Dictionary<string, int> scores = new Dictionary<string, int>();
scores["Ana"] = 12;
scores["Ben"] = 7;
scores["Cleo"] = 15;
scores["Dan"] = 9;
int total = 0;
int count = 0;
foreach (var pair in scores) {
total += pair.Value;
if (pair.Value >= 10) {
count += 1;
}
}
Console.WriteLine(total);
Console.WriteLine(count);
Output:
43
2
12 + 7 + 15 + 9 is 43, and only Ana (12) and Cleo (15) reach 10. Both answers are the same whatever order the loop visits the players in.
Common mistakes
- Adding the pair instead of its value.
total += pairdoesn't compile, becausepairis a key-value pair, not anint. Writetotal += pair.Value. - Lowercase property names. C# names are case sensitive: it is
KeyandValue, notkeyandvalue. - Using
Length. A dictionary reports its number of entries asCount. If you only need the number of entries, you don't need a loop at all. - Counting in the wrong place. The check for 10 or more belongs inside the loop, next to the addition, so it runs once per player.
Key takeaways
foreach (var pair in dict)gives each entry; usepair.Keyandpair.Value.dict.Keysanddict.Valuesloop over just one side.- Don't depend on the order of entries.
- Don't add keys while looping over the dictionary.
