What a loop over a map does
A Kotlin Map stores pairs: each key has one value. A for loop over a map visits every pair once, and the loop body runs once per entry. The usual way to write it is to split each entry into its key and its value right in the loop header:
val stock = mutableMapOf("apples" to 5, "pears" to 2)
for ((fruit, amount) in stock) {
println("$fruit $amount")
}
It prints:
apples 5
pears 2
The parentheses around (fruit, amount) matter. They tell Kotlin to take the entry apart: the key goes into the first name, the value into the second. Without them, for (fruit, amount in stock) is a syntax error, because a loop header names one loop variable unless you destructure it.
Other ways to loop
You do not always need both parts of an entry.
for (fruit in stock.keys)visits only the keys.for (amount in stock.values)visits only the values.for (entry in stock)gives you the whole entry, and you readentry.keyandentry.value.
When you want only the values but still loop over the map itself, write _ in place of the name you do not need: for ((_, amount) in stock). The underscore says "I am skipping this part", and Kotlin does not warn about it. A real name that you never use does get a warning.
About the order
A map made with mutableMapOf keeps its keys in the order they were added, so the loop above printed apples before pears. A hashMapOf does not promise any order. Here is one printed after adding the keys zebra, apple, mango and kiwi, in that order:
{zebra=1, apple=2, kiwi=4, mango=3}
So write loops whose answer does not depend on the order. A total, a count and a yes-or-no check come out the same in any order. If you need sorted output, sort first: for ((k, v) in h.toSortedMap()) visits the keys alphabetically (apple, kiwi, mango, zebra).
Do not change the keys inside the loop
Adding or removing keys while you loop over the same map is a trap. Kotlin stops with a ConcurrentModificationException. For example, removing "apples" from stock inside for ((k, _) in stock) throws it. Changing the value of a key that is already there is fine. If you must remove keys, loop over a copy of the keys, stock.keys.toList(), and remove from the map itself:
val copy = mutableMapOf("a" to 1, "b" to 2, "c" to 3)
for (k in copy.keys.toList()) {
if (k != "b") copy.remove(k)
}
println(copy) // {b=2}
The lesson's task
The map scores holds each player's points. The task is to add every player's points to total and to count, in count, the players with 10 points or more. The names do not matter, so the loop skips them with _.
- Write
for ((_, points) in scores) {and close it with}straight away, so the following steps go inside. - Inside, add
pointstototal. - Still inside, if
pointsis 10 or more, add 1 tocount.
val scores = mutableMapOf("Ana" to 12, "Ben" to 7, "Cleo" to 15, "Dan" to 9)
var total = 0
var count = 0
for ((_, points) in scores) {
total += points
if (points >= 10) {
count += 1
}
}
println(total)
println(count)
The output is:
43
2
The points are 12, 7, 15 and 9, which add up to 43. Two players, Ana and Cleo, have 10 or more. The total and the count are the same whichever order the players come in, so the loop is safe to rely on.
Common mistakes
- Forgetting the parentheses around the pair in the loop header.
- Putting the line that updates
countoutside the loop, which counts only the last player's result instead of every player's. - Adding or removing keys of
scoresinside the loop. - Declaring
totalwithval. It changes during the loop, so it needsvar.
Key takeaways
for ((key, value) in map)visits every entry once.- Use
_for the part you do not need, or loop overmap.keysormap.values. - Only write code that does not depend on the order, unless you sort first.
- Do not add or remove keys while looping over the map.
