1. Introduction

Scala is renowned for its robust collections standard library, which offers many effective methods for manipulating and performing actions on collections.

In this tutorial, we’ll explore two such methods, filter() and takeWhile(). Both are useful for selecting elements from a collection based on specific criteria. However, they operate differently.

2. filter()

The filter() function iterates through every element in a collection and applies the given predicate. It returns a new collection containing only the elements where the predicate evaluates to true. Let’s look at a simple example to understand its functionality:

val numbers = List(1, 2, 3, 4, 5, 6)
val oddNumbers = numbers.filter(_ % 2 != 0)
oddNumbers shouldBe List(1, 3, 5)

In the above code, we use the filter() method on a List to extract only the odd numbers.

Here, we used a finite collection for the filtering operation. Now, let’s look at what happens if we apply the filter() method to an infinite or unbounded collection. We can use a LazyList to create an infinite collection. Let’s look at an example:

val infiniteNumbers = LazyList.from(1)
infiniteNumbers.filter(_ < 100).toList // will not terminate

In this scenario, we create an infinite collection of natural numbers using the LazyList.from() method. Subsequently, we can apply the filter() method to this infiniteNumbers variable. However, due to the nature of an infinite sequence, the filter() operation continues iterating through the elements endlessly. Consequently, the toList() method called on the filtered lazy list never completes.

3. takeWhile()

The takeWhile() method also applies the given predicate on the collection. However, it traverses the collection sequentially, stopping when the predicate function evaluates to false for the first time. Let’s look at a simple example:

val numbers = List(1, 2, 3, 4, 5, 6)
val numbersBeforeFirstEven = numbers.takeWhile(_ % 2 != 0)
numbersBeforeFirstEven shouldBe List(1)

The above example is similar to the code we used for the filter() method to get odd numbers. However, instead of the filter() method, we use takeWhile(). The takeWhile() applies the predicate and returns the elements until the condition is no longer satisfied for the first time, thereby returning a list containing only element 1.

Let’s try with another sample list to observe the behavior of takeWhile():

val numbers = List(1, 3, 4, 5, 6)
val numbersBeforeFirstEven = numbers.takeWhile(_ % 2 != 0)
numbersBeforeFirstEven shouldBe List(1, 3)

Here, the takeWhile() returned List(1, 3) because the predicate failed only for the third element in the list, which is the number 4.

Now, let’s look at what happens if we apply the takeWhile() function to an infinite list:

val infiniteNumbers = LazyList.from(1)
val first100 = infiniteNumbers.takeWhile(_ < 100).toList
first100 shouldBe (1 to 99).toList

In this scenario, the test passes because the takeWhile() terminates execution immediately once the predicate fails, effectively avoiding any potential for infinite processing.

It’s important to understand that element ordering influences the outcomes of the takeWhile() operation, unlike the filter() method, which remains unaffected by the order of elements.

4. Summary

Below is a table summarizing the main differences between the filter() and the takeWhile() methods for quick reference:

Behavior

filter()

takeWhile()

Operation

Selects elements based on condition

Selects elements until a condition is false

Iteration

Iterates over all elements of the collection

Iterates only until the first failure of the predicate

Impact of Element Order

No Impact

Impacted

Termination

No Early Termination

Early Termination

Practical Usecase Example

Filtering data based on different fields

Analyzing time-series data to determine when a threshold is exceeded in temperature readings

5. Conclusion

In this article, we explored the differences between filter() and takeWhile() methods on the collection. While both methods offer powerful ways to select elements from collections based on specific criteria, they differ in their behaviors and performance characteristics. The filter() method is ideal for selectively retaining elements that satisfy a condition, whereas the takeWhile() method excels at extracting elements until a condition becomes false for the first time.

By effectively using these methods, we can write cleaner, more concise code and improve the performance of our Scala applications.

As always, the sample code used in this tutorial is available over on GitHub.