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.