1. Overview

Collections in Scala provide various built-in functions for creating, transforming and validating them. f**orall() is a built-in function that helps us validate data within a collection.

In this tutorial, we’ll explore its usage through some examples. We’ll be using the Scala REPL to run all the examples.

2. Using f**orall() With Collections

We can use the forall() function to validate that all the elements of a collection satisfy a predicate. This means that in order to return true, all the elements must fulfil the predicate.

Let’s see this with an example:

scala> val seq = Seq(1,2,3,4,5)
seq: Seq[Int] = List(1, 2, 3, 4, 5)

scala> seq.forall(_ < 10)
res0: Boolean = true

Here, we use forall() to see if all the elements of the sequence are less than 10. Since this is true for all the elements, the function returns true.

Now, let’s see the exact opposite, by checking if the elements of the sequence are all less than 5:

scala> seq.forall(_ < 5)
res1: Boolean = false

The elements cannot satisfy the predicate, since number 5 is not less than 5, and the function returns false.

Although forall() is pretty straightforward, there’s one tricky case. What will it return if we use it in an empty collection? Since there is no element to satisfy the predicate would it return false? Let’s find out with another example:

scala> val emptySeq = Seq.empty[Int]
emptySeq: Seq[Int] = List()

scala> emptySeq.forall(_ > 10)
res2: Boolean = true

As mentioned in the function’s documentation, forall() returns true if the collection is empty or the given predicate holds for all the elements, otherwise it returns false.

By checking the function’s implementation, we can see the reason:

def forall(p: A => Boolean): Boolean = {
    var res = true
    val it = iterator
    while (res && it.hasNext) res = p(it.next())
    res
}

The function starts by assuming that the predicate is satisfied and it will become false when it finds an element that doesn’t satisfy it.

3. Using f**orall() With Map

We can also use forall() with Map, allowing us to validate both keys and values:

scala> val map = Map(1 -> 1, 2 -> 2, 3 -> 3, 4 -> 4, 5 -> 5)
map: scala.collection.immutable.Map[Int,Int] = Map(5 -> 5, 1 -> 1, 2 -> 2, 3 -> 3, 4 -> 4)

scala> map.forall { case (key, value) => key < 10 }
res3: Boolean = true

scala> map.forall { case (key, value) => value < 10 }
res4: Boolean = true

scala> map.forall { case (key, value) => key + value < 20 }
res5: Boolean = true

We created a Map with the same number of Int keys and values for simplicity. First, we performed validation on the keys. All the keys satisfy the predicate and the function returns true. Then, we performed the same validation on the values. Again, all the values satisfy the predicate and the function returns true. From the last example, we can see that we can even perform validations on both the keys and their values.

Using the same Map, let’s see some examples where forall() returns false because we don’t satisfy the predicate condition:

scala> map.forall { case (key, value) => key < 5 }
res6: Boolean = false

scala> map.forall { case (key, value) => value < 5 }
res7: Boolean = false

scala> map.forall { case (key, value) => key + value < 10 }
res8: Boolean = false

In the examples above, we don’t satisfy the predicate for all the elements, keys, values, or their combination and the function returns false.

Finally, let’s see what happens if we use forall() on an empty Map:

scala> val emptyMap: Map[Int, Int] = Map.empty
emptyMap: Map[Int,Int] = Map()

scala> emptyMap.forall { case (key, value) => key > 10 }
res9: Boolean = true

scala> emptyMap.forall { case (key, value) => value > 10 }
res10: Boolean = true

scala> emptyMap.forall { case (key, value) => key + value > 10 }
res11: Boolean = true

As we can see, the function again returns true for an empty Map.

4. Using f**orall() With Option

Like most of the functions that we can use with collections*,* we can also use forall() with Option:

scala> val optionalInt = Some(5)
optionalInt: Some[Int] = Some(5)

scala> optionalInt.forall(_ < 10)
res12: Boolean = true

The function returns true, since the value of the Option satisfies the predicate*.*

If our value doesn’t satisfy the predicate, we notice the exact same behavior as we did with collections:

scala> optionalInt.forall(_ < 5)
res13: Boolean = false

And finally, let’s see what happens with an empty Option:

scala> val emptyOption: Option[Int] = None
emptyOption: Option[Int] = None

scala> emptyOption.forall(_ < 5)
res14: Boolean = true

So, as with collections, if we use forall() function with an empty Option, it returns true.

5. Using f**orall() vs. Using e**xists()

Another built-in function for collections, Option and Map worth noticing is the exists() function*.* exists() has the exact same interface as forall(). It takes a predicate and returns a Boolean, but its functionality is different.

While forall() validates the predicate for all the elements of a collection, exists() only needs to find one:

scala> val newSeq = Seq(1, 2, 3, 4, 5, 6)
newSeq: Seq[Int] = List(1, 2, 3, 4, 5, 6)

scala> newSeq.exists(_ > 5)
res15: Boolean = true

scala> newSeq.exists(_ > 10)
res16: Boolean = false

newSeq has exactly 1 element that’s greater than 5, the number 6, so we satisfy the predicate for at least 1 element and the function returns true. On the other hand, since there’s no element that’s greater than 10, the predicate is not satisfied and the function returns false for the second case.

Now, let’s see what happens if we use the exists() function on an empty collection:

scala> val newEmptySeq: Seq[Int] = Seq.empty
newEmptySeq: Seq[Int] = List()

scala> newEmptySeq.exists(_ > 5)
res17: Boolean = false

We don’t satisfy the predicate, so the function returns false. By comparing its implementation with forall(), we can see that exists() assumes that the predicate isn’t satisfied and stops iterating when it finds an element that satisfies it:

def exists(p: A => Boolean): Boolean = {
    var res = false
    val it = iterator
    while (!res && it.hasNext) res = p(it.next())
    res
}

6. Conclusion

In this article, we saw how we can use the forall() function with collections, Option and Map and its practical difference with the exists() function*.*


« 上一篇: Scala ListSet 指南
» 下一篇: 使用 ScalaTest Runner