Tech and Media Labs
This site uses cookies to improve the user experience.




Scala for

Jakob Jenkov
Last update: 2014-05-25

The Scala for loop also executes a certain block of code, as long as a certain condition is true. However, it works a bit differently than the while loop. Here is an example:

for(i <- 1 to 10) {
    println("i is " + i);
}

This for loop iterates 10 times and for each iteration assigns the val i the next number in the range.

The i <- construct is called a generator. For each iteration it initializes the val i with a value.

The 1 to 10 is called a Range type. It returns a range containing the values from 1 to 10, including both numbers. Range types are described in more detail in a different text.

The 1 to 10 is actually a method call that returns the Range type. It is equivalent to

(1).to(10);

How this works is described in more detail in the text on Scala's support for domain specific languages (DSL).

Omitting the { } in for Loops

You can omit the { } in for loops, if the body of the loop consists of a single statement. Here is an example:

for(i <- 1 to 10)
    println("i is " + i);

to vs. until

You can use either the keyword to or until when creating a Range object. The difference is, that to includes the last value in the range, whereas until leaves it out. Here are two examples:

for(i <- 1 to 10) {
    println("i is " + i);
}

for(i <- 1 until 10) {
    println("i is " + i);
}

The first loop iterates 10 times, from 1 to 10 including 10.

The second loop iterates 9 times, from 1 to 9, excluding the upper boundary value 10.

Iterating Collections and Arrays

You can iterate a collection or array using the for loop, like this:

var myArray : Array[String] = new Array[String](10);

for(i <- 0 until myArray.length){
    myArray(i) = "value is: " + i;
}

for(value : String <- myArray ) {
    println(value);
}

First an array is created.

Second, each element in the array is initialized to the text "value is: " with the index of the element appended.

Third, the array is iterated using the generator notation ( <- ). For each iteration the next element in the array is assigned to the val value, and then printed to the console.

Filtering for Loops

In Scala for loops it is possible to apply filtering to the iteration of a collection or array. Here is how:

var myArray : Array[String] = new Array[String](10);

for(i <- 0 until myArray.length){
    myArray(i) = "value is: " + i;
}

for(value : String <- myArray if value.endsWith("5")) {
    println(value);
}

Notice the if value.endsWith("5") marked in bold. This condition, or filter, means that the for loop only executes its body if the string assigned value ends with the text "5". Only one of the array elements ends with the text "5", so the for loop body is only executed once.

The filtering for loop above is equivalent to the following code:

for(value : String <- myArray) {
    if value.endsWith("5"){
        println(value);
    }
}

Personally, I actually prefer the good old way of writing it (the second way). While the new way is a bit shorter, it is not really much easier to read. Especially not if the conditions are more complex, as you will see hereafter.

Multiple for Loop Filters

You can apply multiple for loop filters like this:

for(value : String <- myArray
    if value.endsWith("5");
    if value.indexOf("value") != -1 ) {

    println(value);
}

Notice the two if-statements inside the for loop declaration. They are separated by a semicolon. Both of these conditions now have to be true, for the for loop body to be executed.

The for loop above is equivalent to this old school for loop:

for(value : String <- myArray) {
    if( value.endsWith("5") && value.indexOf("value") != -1){
        println(value);
    }
}

Personally, I still find the second, old school version easier to read and understand.

Nested Iteration

It is possible to do nested looping in a single for loop. Imagine you had an array of arrays. You could then iterate them like this:

var myArray : Array[Array[String]] = new Array[Array[String]](10);

for(i <- 0 until myArray.length){
    myArray(i) = new Array[String](10);
    
    for(j <- 0 until myArray(i).length){
      myArray(i)(j) = "value is: " + i + ", " + j;
    }
}

for(anArray : Array[String] <- myArray;
    aString : String        <- anArray ) {

    println(aString);
}

First, an array of arrays is created, and initialized with arrays, and string values.

Second, the array of arrays is iterated using a nested loop. First, each String array is in the array of arrays is assigned to the val anArray. Then each String value of each String array is assigned to the valu aString.

The above nested loop is equivalent to this old school nested for loop:

for(anArray : Array[String] <- myArray) {
    for(aString : String <- anArray) {
        println(aString);
    }
}

Again, I still prefer the old school for loop version.

Midstream Variable Binding

It is possible to bind values to a variable in the middle of a nested iteration, like this:

for(anArray : Array[String] <- myArray;
    aString : String        <- anArray;
    aStringUC = aString.toUpperCase()
    if aStringUC.indexOf("VALUE") != -1;
    if aStringUC.indexOf("5") != -1
    ) {

    println(aString);
}

The aString.toUpperCase() result is needed by two filter conditions. Rather than compute the .toUpperCase() value twice, by nesting them inside each if-statement, the uppercase version of aString is computed just once, and assigned to the variable aStringUC. The two filter-conditions (if-statements) can now refer to this "mid stream" computed value.

You could achieve the same effect using this old school for loop:

for(anArray : Array[String] <- myArray) {
  for(aString : String <- anArray) {

    var aStringUC = aString.toUpperCase();

    if(aStringUC.indexOf("5") != -1 && aStringUC.indexOf("VALUE") != -1){
      println(aString);
    }
  }
}

Once again, I belive this old school for loop is actually more readable.

Jakob Jenkov




Copyright  Jenkov Aps
Close TOC