Scala filter: How to use the ‘filter’ method to filter Scala collections

This is an excerpt from the 1st Edition of the Scala Cookbook (partially modified for the internet). This is Recipe 10.17, “How to use filter to Filter a Scala Collection”

Problem

You want to filter the items in a collection to create a new collection that contains only the elements that match your filtering criteria.

Solution

As listed in Recipe 10.3, “Choosing a Collection Method to Solve a Problem”, a variety of methods can be used to filter the elements of an input collection to produce a new output collection. This recipe demonstrates the filter method.

To use filter on your collection, give it a predicate to filter the collection elements as desired. Your predicate should accept a parameter of the same type that the collection holds, evaluate that element, and return true to keep the element in the new collection, or false to filter it out. Remember to assign the results of the filtering operation to a new variable.

A predicate is just a method (or function) that returns a boolean value.

For instance, the following example shows how to create a list of even numbers from an input list using a modulus algorithm:

scala> val x = List.range(1, 10)
x: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)

// create a list of all the even numbers in the list
scala> val evens = x.filter(_ % 2 == 0)
evens: List[Int] = List(2, 4, 6, 8)

As shown, filter returns all elements from a sequence that return true when your function/predicate is called. There’s also a filterNot method that returns all elements from a list for which your function returns false.

Tip: You can think of filter as meaning retain.

Discussion

The main methods you can use to filter a Scala collection are listed in Recipe 10.3 of the Scala Cookbook, and are repeated here for your convenience:

  • collect
  • diff
  • distinct
  • drop
  • dropWhile
  • filter
  • filterNot
  • find
  • foldLeft
  • foldRight
  • head
  • headOption
  • init
  • intersect
  • last
  • lastOption
  • reduceLeft
  • reduceRight
  • remove
  • slice
  • tail
  • take
  • takeWhile
  • union

Unique characteristics of filter compared to these other methods include:

  • filter walks through all of the elements in the collection; some of the other methods stop before reaching the end of the collection
  • filter lets you supply a predicate to filter the elements

How you filter the elements in your collection is entirely up to your algorithm. The following examples show a few ways to filter a list of strings:

scala> val fruits = Set("orange", "peach", "apple", "banana")
fruits: scala.collection.immutable.Set[java.lang.String] = Set(orange, peach, apple, banana)

scala> val x = fruits.filter(_.startsWith("a"))
x: scala.collection.immutable.Set[String] = Set(apple)

scala> val y = fruits.filter(_.length > 5)
y: scala.collection.immutable.Set[String] = Set(orange, banana)

Your filtering function can be as complicated as needed. When your algorithm gets long, you can pass a multiline block of code into filter:

scala> val list = "apple" :: "banana" :: 1 :: 2 :: Nil
list: List[Any] = List(apple, banana, 1, 2)

scala> val strings = list.filter {
     |     case s: String => true
     |     case _ => false
     | }
strings: List[Any] = List(apple, banana)

You can also put your algorithm in a separate method (or function) and then pass it into filter:

def onlyStrings(a: Any) = a match {
    case s: String => true
    case _ => false
}
val strings = list.filter(onlyStrings)

The following example demonstrates that you can filter a list as many times as needed:

def getFileContentsWithoutBlanksComments(canonicalFilename: String): List[String] = {
    io.Source.fromFile(canonicalFilename)
             .getLines
             .toList
             .filter(_.trim != "")
             .filter(_.charAt(0) != '#')
}

The two keys to using filter are:

  • Your algorithm should return true for the elements you want to keep and false for the other elements
  • Remember to assign the results of the filter method to a new variable; filter doesn’t modify the collection it’s invoked on

See Also

  • The collect method can also be used as a filtering method. Because it uses partial functions, it’s described in detail in Recipe 9.8, “Creating Partial Functions”.