Scala for/yield examples (for-loop and yield syntax)

I just found some notes from when I first began working with Scala, and I was working with the yield keyword in for loops. If you haven't worked with something like yield before, it will help to know how it works. Here's a statement of how the yield keyword works in for loops, from the book, Programming in Scala:

For each iteration of your for loop, yield generates a value which will be remembered. It's like the for loop has a buffer you can’t see, and for each iteration of your for loop another item is added to that buffer. When your for loop finishes running, it will return this collection of all the yielded values. The type of the collection that is returned is the same type that you were iterating over, so a Map yields a Map, a List yields a List, and so on.

Also, note that the initial collection is not changed; the for/yield construct creates a new collection according to the algorithm you specify.

Basic Scala 'for' loop examples

Given that background information, let’s look at a few for/yield examples. First, this example just yields a new collection that’s identical to the collection I’m looping over:

scala> for (i <- 1 to 5) yield i
res10: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3, 4, 5)

Nothing too exciting there, but it’s a start. Next, let’s double every element in our initial collection:

scala> for (i <- 1 to 5) yield i * 2
res11: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4, 6, 8, 10)

As another example, here’s what the Scala modulus operator does in a for/yield loop:

scala> for (i <- 1 to 5) yield i % 2
res12: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 0, 1, 0, 1)

for-loop/yield examples over a Scala Array

I mentioned in my description that the for loop yield construct returns a collection that is the same as the collection it is given. To demonstrate this, let’s look at the same examples with a Scala Array. Note the type of the collection that is yielded, and compare it to the previous examples:

scala> val a = Array(1, 2, 3, 4, 5)
a: Array[Int] = Array(1, 2, 3, 4, 5)

scala> for (e <- a) yield e
res5: Array[Int] = Array(1, 2, 3, 4, 5)

scala> for (e <- a) yield e * 2
res6: Array[Int] = Array(2, 4, 6, 8, 10)

scala> for (e <- a) yield e % 2
res7: Array[Int] = Array(1, 0, 1, 0, 1)

As you can see, in these examples an Array[Int] was yielded, while in the earlier examples an IndexedSeq[Int] was returned.

'for' loop, yield, and guards (for-loop ‘if’ conditions)

If you’re familiar with the Scala for comprehension syntax, you know that you can add if statements to your for loop construct. Tests like these are often referred to as “guards,” and you can combine them with the yield syntax, as shown here:

scala> val a = Array(1, 2, 3, 4, 5)
a: Array[Int] = Array(1, 2, 3, 4, 5)

scala> for (e <- a if e > 2) yield e
res1: Array[Int] = Array(3, 4, 5)

As you can see, adding the if e > 2 guard condition limits the Array we return to the three elements shown.

A real-world example

I don’t know if this will make sense out of context, but if you’d like to see a real-world use of a for/yield loop, here you go:

def getQueryAsSeq(query: String): Seq[MiniTweet] = {
    val queryResults = getTwitterInstance.search(new Query(query))
    val tweets = queryResults.getTweets  // java.util.List[Status]
    for (status <- tweets) yield ListTweet(status.getUser.toString, status.getText, status.getCreatedAt.toString)
}

This code uses the JavaConversions package to convert the java.util.List[Status] I get back from Twitter4J into a Seq[MiniTweet]. The loop actually returns a Buffer[ListTweet], which is a Seq[ListTweet]. A MiniTweet is just a small version of a Twitter tweet, with the three fields shown.

Summary: Scala for-loop and yield examples

If you’re familiar with Scala’s for-loop construct, you know that there’s also much more work that can be performed in the first set of parentheses. You can add if statements and other statements there, such as this example from the book Programming in Scala:

def scalaFiles = 
    for {
        file <- filesHere
        if file.getName.endsWith(".scala")
    } yield file

I'll try to share more complicated examples like this in the future, but for today I wanted to share some simple for/yield examples, something like “An introduction to the Scala yield keyword.”

Summary: Scala’s ‘yield’ keyword

As a quick summary of the yield keyword:

  • For each iteration of your for loop, yield generates a value which is remembered by the for loop (behind the scenes, like a buffer).
  • When your for loop finishes running, it returns a collection of all these yielded values.
  • The type of the collection that is returned is the same type that you were iterating over.

I hope these examples have been helpful.