Why these two exist
You have looped over positions with for (i in 0 until meals.size) and then read meals[i] to get the item. Kotlin has a shorter way to get both the position and the item, and another for walking two lists together.
withIndex()
withIndex() gives each item together with its index. The loop unpacks the two parts, the same way val (name, age) = pet unpacks a pair:
val fruits = listOf("apple", "kiwi", "plum")
for ((i, fruit) in fruits.withIndex()) {
println("$i $fruit")
}
0 apple
1 kiwi
2 plum
The index comes first and starts at 0. For a numbered list that starts at 1, print i + 1: println("${i + 1}. $fruit") gives 1. apple, then 2. kiwi, then 3. plum.
zip
a.zip(b) walks two lists side by side. It pairs the first item of a with the first item of b, then the second items, and so on. The result is a list of Pairs, which the loop unpacks:
val prices = listOf(3, 1, 2)
for ((fruit, price) in fruits.zip(prices)) {
println("$fruit costs $price")
}
apple costs 3
kiwi costs 1
plum costs 2
zip stops at the end of the shorter list. With two prices instead of three, fruits.zip(listOf(3, 1)) is [(apple, 3), (kiwi, 1)]: the extra fruit is skipped, with no error.
Using both together
fruits.zip(prices).withIndex() gives an index and a pair on each pass. A for line can't unpack a pair inside a pair: writing for ((i, (fruit, price)) in ...) is a syntax error. Unpack the pair on the first line of the loop's body:
for ((i, pair) in fruits.zip(prices).withIndex()) {
val (fruit, price) = pair
println("${i + 1}. $fruit costs $price")
}
1. apple costs 3
2. kiwi costs 1
3. plum costs 2
The exercise
A quiz show keeps its players in players and their points in scores, in the same order. First print the players as a numbered list from 1, with withIndex(). Then print each player with their points, with zip.
class Solution {
fun mainFunction() {
val players = mutableListOf("Ana", "Ben", "Cleo")
val scores = mutableListOf(12, 9, 15)
for ((i, player) in players.withIndex()) {
println("${i + 1}. $player")
}
for ((player, points) in players.zip(scores)) {
println("$player has $points points")
}
}
}
The output:
1. Ana
2. Ben
3. Cleo
Ana has 12 points
Ben has 9 points
Cleo has 15 points
The first loop needs the position, so it uses withIndex(). The second needs the matching score, not the position, so zip is enough and no index is involved.
Why does withIndex() exist when you can write meals[i]? With a plain index loop it is easy to make a slip, such as going one step past the last item. withIndex() only hands you items that exist. It also works on any list the same way, and the loop says plainly that it needs both the position and the value.
Likewise, zip saves you from indexing two lists with the same i. Instead of players[i] and scores[i] in one loop, the pair arrives ready: the first name goes with the first score, and so on. The two lists must be kept in the same order for this to be right, which the exercise promises.
Common mistakes
- Printing
iinstead ofi + 1, so the list starts at 0. - Swapping the unpacked names. In
(i, player)the index is first. Writing(player, i)compiles forwithIndex(), butplayerthen holds a number. - Lists of different lengths in
zip. The longer list's extra items are dropped silently, so check the lengths if that matters.
Key takeaways
withIndex()gives(index, item), with the index first, from 0.a.zip(b)pairs items by position and stops at the shorter list.- To combine them, unpack the inner pair in a separate line.
