zipWithIndex makes looping in Scala fun

TIL how to use zipWithIndex in Scala.

In this blogpost I will explain what I learned about zipWithIndex with a real use case. The project of the use case is made in Playframework. I have changed the context of the use case, because it is confidential.

Current situation

I have a table with the title of an order on every row.

@(orders: List[Order])

<table>
@for(order <- orders) {
    <tr>
        <td>@order.title</td>
    </tr>
}
</table>

Requirement

Highlight the first three orders in the table.

Note that the order of the elements in the list does not change.

My initial solution

So, to make the requirement more specific: if the index is 0, 1 or 2 add the css class highlight. That sounds easy, right?

Well, I never needed the index of an element, when looping in Scala, before. I need a way to have some sort of index in each iteration. So I went googling.

I found that I could use until:

for(i <- 0 until 3)

With until, the final number is not included, because it is an exclusive bound. The output:

0
1
2

Hmm, I thought the exclusive bound may confuse other developers in my team. I went looking further to see if the code could be more clearer.

I also found that I could use to, which specifies to what number the loop advances—so I can iterate over a range of numbers.

Now I have an increasing number in the for, I only need to get an element from a certain index of the list. 😕

With defining and get(index) I can get an element at a certain index from the list.

I did a pull request with my initial solution:

@(orders: List[Order])

<table>
@for(orderIndex <- 0 to orders.size {
    @defining(orders.get(underBidIndex)) { order =>
        <tr @if(orderIndex < 3){class="highlight"}>
            <td>@order.title</td>
        </tr>
    }
}
</table>

With great colleagues, comes great code reviews

I think you should learn and use: zipWithIndex. Sample:

@for((element, index) <- list.zipWithIndex) {
@* then continue something like *@
@if(index = 5){class=""}
}

I highly appreciate this kind of feedback, because for my own development, it is much better than pre-chewed answers—from which I would not learn anything. I could have said, "Mwah, this works, why change it?!", but that is not me. So I exactly did what my colleague told me to do: learn and use zipWithIndex.

Luckily, the Scala Cookbook was not far away. Recipe 10.11, “How to Use zipWithIndex or zip to Create Loop Counters”. Awesome.

You can use the zipWithIndex method to create a counter automatically. Assuming you have a sequential collection, you can print the elements in the collection with a counter using the zipWithIndex and foreach methods. An example in Scala:

list.zipWithIndex.foreach {
    case(element, index) => println(s"index $index is element $element")
}

You can also use zipWithIndex with a for loop:

for ((element, index) <- list.zipWithIndex) {
    println(s"index $index is element $element")
}

Translating this to syntax for Play Scala templates:

@for((element, index) <- list.zipWithIndex) {
    <p>index @index is element @element</p>
}

Note that when using zipWithIndex, the counter always starts at 0.

Final commit

So, my final commit to the PR:

@(orders: List[Order])

<table>
@for((order, index) <- orders.zipWithIndex) {
    <tr @if(index < 3){class="highlight"}>
        <td>@order.title</td>
    </tr>
}
</table>

I really think zipWithIndex makes the code look clean. WDYT? 👨🏻‍💻


SHARE THIS ARTICLE