How to use multiple generators in Scala ‘for’ expressions (loops)

A cool thing about Scala for loops — what I’ll more-accurately call for expressions in this article — is that you can have multiple generators. What’s also very cool about them is how they work.

For example, imagine that you have these two values:

val nums = Seq(1,2,3)
val letters = Seq('a', 'b', 'c')

An interesting question then becomes, “What is the type of res in this expression?”:

val res = for {
    n <- nums
    c <- letters
} yield (n, c)

The answer is that the type of res is:

Seq[(Int, Char)]

This is because each iteration of the expression yields a Tuple2 of (n, c). Because nums and letters both contain three elements, the loop will yield a total of nine elements. (I know this because I know the answer to the next question.)

Which is a nice segway to Question #2: If you print res like this, what will it print?:

res.foreach(println)

As I wrote in the Scala Cookbook, the way the Scala for expression shown above works is that it’s similar to this:

for (n <- nums) {
    for (c <- letters) {
        println(n + ", " + c)
    }
}

As a result, its output is:

(1,a)
(1,b)
(1,c)
(2,a)
(2,b)
(2,c)
(3,a)
(3,b)
(3,c)

As you can see, the for expression holds the value from the first generator constant while it loops through all of the elements from the second generator. Once those are exhausted, it moves to the next element from the first generator (nums), and then begins iterating over the elements from the second generator (letters) all over again.

Source code: Multiple generators in Scala for expressions

I could go on talking about using multiple generators in for expressions for quite some time, but to keep this short I’ll just share the following complete Scala source code example.

If you’re struggling with multiple generators in Scala for loops/expressions, I encourage you to copy and paste this code into your favorite IDE, and then work with these examples until you understand them.

Notice that the first example yields a Tuple2, and the second generator example yields a List. I did this so you can see at least two different data structures. The third example just shows that you can use the same data source as a generator multiple times in a for expression.

With that little introduction, here’s the source code:

object MultipleGenerators extends App {

    val nums = Seq(1,2,3)
    val letters = Seq('a', 'b', 'c')


    // 1 - Seq[(Int, Char)]
    println("\n__ 1 __")
    val res = for {
        n <- nums
        c <- letters
    } yield (n, c)
    res.foreach(a => println("item: " + a))


    // 2 - Seq[List[Char]]
    println("\n__ 2 __")
    val moreLetters = Seq('x', 'y', 'z')
    val res2 = for {
        c1 <- letters
        c2 <- moreLetters
    } yield c1 :: c2 :: Nil
    res2.foreach(a => println("item: " + a))


    // 3
    println("\n__ 3 __")
    val res3 = for {
        n1 <- nums
        n2 <- nums
    } yield (n1, n2)
    res3.foreach(a => println("item: " + a))

}

Inspiration for this post

FWIW, my inspiration for writing this blog post comes from the book, Programming in Scala. In Chapter 23, the authors share some code that looks like this:

// not completely accurate, but close (has dups)
val authorsWhoHaveWrittenAtLeastTwoBooksV1 = for {
    b1 <- books       //gen
    b2 <- books       //gen
    if b1 != b2       //filter
    a1 <- b1.authors  //gen
    a2 <- b2.authors  //gen
    if a1 == a2       //filter
} yield a1
println("\nAuthors who have written at least two books:")
authorsWhoHaveWrittenAtLeastTwoBooksV1.foreach(println)

After seeing this code it hit me (again) how powerful for expressions are.

Summary

In summary, if you needed to see some examples of how to use multiple generators in Scala for expressions, I hope this is helpful.