Start learning free

LESSON 275 OF 677 · TUPLES, SORTING & BUILT-INS

zipWithIndex and zip in Scala: Loop With Index or Two Lists

Lesson Read the full explanation

TL ; DR:

zipWithIndex gives each item with its index. zip walks two ArrayBuffers side by side.


val fruits = scala.collection.mutable.ArrayBuffer("apple", "kiwi", "plum")
val prices = scala.collection.mutable.ArrayBuffer(3, 1, 2)

for ((fruit, i) <- fruits.zipWithIndex) {
    println(s"${i + 1}. $fruit")    // 1. apple, 2. kiwi, 3. plum
}

for ((fruit, price) <- fruits.zip(prices)) {
    println(s"$fruit $price")       // apple 3, kiwi 1, plum 2
}


Full lesson:

You have looped with for (i <- meals.indices). Then meals(i) gave the item. Scala has a shorter way to get both.

zipWithIndex pairs each item with its index. The loop unpacks each pair into two names, as val (name, age) = pet does.

for ((meal, i) <- meals.zipWithIndex) {
    println(s"$i $meal")
}

Mind the order. The item comes first and the index second. Writing (i, meal) swaps the names, and the output comes out wrong.

The index starts at 0, so print i + 1 for a numbered list.

a.zip(b) walks two ArrayBuffers side by side. It pairs the first item of a with the first item of b. Then it pairs the second items, and so on. Each pair is a tuple.

for ((fruit, price) <- fruits.zip(prices)) {
    println(s"$fruit costs $price")
}

zip stops at the end of the shorter one. The extra items are skipped, with no error.

The two work together. Zip first, then number the pairs with zipWithIndex. Each item is then a pair and an index, so the names nest.

for (((fruit, price), i) <- fruits.zip(prices).zipWithIndex) {
    println(s"${i + 1}. $fruit costs $price")
}

Your task

A quiz show keeps its players in the ArrayBuffer players. Their points are in scores, in the same order.

First, print the players as a numbered list from 1, like 1. Ana. Use zipWithIndex.

Then print each player with their points, like Ana has 12 points. Use zip.

Expected output:

1. Ana
2. Ben
3. Cleo
Ana has 12 points
Ben has 9 points
Cleo has 15 points
STEP 1 OF 4
    Ctrl + Enter to check

    SCALA · TUPLES, SORTING & BUILT-INS

    Enumerate And Zip in Scala, explained

    Getting the item and its index

    You can loop over an index with for (i <- meals.indices) and then read meals(i). Scala has a shorter way to get both at once. The method zipWithIndex pairs each item with its index, and the loop unpacks each pair into two names, as val (name, age) = pet does for a tuple:

    for ((meal, i) <- meals.zipWithIndex) {
        println(s"$i $meal")
    }
    

    For a buffer ArrayBuffer("Ana", "Ben", "Cleo"), zipWithIndex builds ArrayBuffer((Ana,0), (Ben,1), (Cleo,2)): a buffer of pairs.

    Mind the order. The item comes first and the index second. If you write (i, meal) instead, the names are swapped: i holds the item and meal holds the number. With strings, i + 1 then compiles, because + joins a string and a number, and the output comes out wrong. For ArrayBuffer("apple", "bread") a loop written as for ((i, item) <- items.zipWithIndex) printing ${i + 1}. $item gives apple1. 0 and bread1. 1.

    The index starts at 0, so print i + 1 when you want a numbered list that starts at 1.

    Walking two buffers together

    a.zip(b) pairs the first item of a with the first item of b, then the second items, and so on. Each pair is a tuple:

    for ((fruit, price) <- fruits.zip(prices)) {
        println(s"$fruit costs $price")
    }
    

    zip stops at the end of the shorter buffer. The extra items are skipped without any error: zipping three names with two numbers gives two pairs.

    Using both together

    You can zip first and then number the pairs with zipWithIndex. Each item is then a pair together with an index, so the names nest in the loop:

    for (((fruit, price), i) <- fruits.zip(prices).zipWithIndex) {
        println(s"${i + 1}. $fruit costs $price")
    }
    

    With the players and scores of the exercise below, a loop like this that prints ${i + 1}. $player has $points gives 1. Ana has 12, 2. Ben has 9 and 3. Cleo has 15. The inner parentheses hold the pair from zip, and the outer ones hold that pair and the index.

    The exercise

    A quiz show keeps its players in the buffer players, and their points in scores, in the same order. First print the players as a numbered list from 1, like 1. Ana, with zipWithIndex. Then print each player with their points, like Ana has 12 points, with zip.

    class Solution {
        def mainFunction(): Unit = {
            val players = scala.collection.mutable.ArrayBuffer("Ana", "Ben", "Cleo")
            val scores = scala.collection.mutable.ArrayBuffer(12, 9, 15)
            for ((player, i) <- players.zipWithIndex) {
                println(s"${i + 1}. $player")
            }
            for ((player, points) <- players.zip(scores)) {
                println(s"$player has $points points")
            }
        }
    }
    

    The output is:

    1. Ana
    2. Ben
    3. Cleo
    Ana has 12 points
    Ben has 9 points
    Cleo has 15 points
    

    The second loop doesn't need an index, because zip already lines up each player with the points at the same position.

    Why not index into both buffers?

    You could write for (i <- players.indices) and read players(i) and scores(i). That gives the same output, but it asks you to keep the index in step by hand, and a typo in one of the two reads goes unnoticed. With zip the pairing is done for you, and each name in the loop says what it is. Use the index form when you really need the position, for example to look at the item before or after the current one.

    Common mistakes

    • Swapping the names so the index comes first.
    • Forgetting that the index starts at 0 and printing 0. Ana.
    • Zipping buffers of different lengths and not noticing that the longer one is cut short.
    • Putting the parentheses wrong when nesting: zip(...).zipWithIndex gives ((item, price), i), not (item, price, i).

    Key takeaways

    • zipWithIndex gives (item, index) pairs, and the index starts at 0.
    • a.zip(b) pairs items by position and stops at the shorter buffer.
    • Unpack the pairs right in the for loop.
    Keep going with Scala500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free