The Scala List class filter
method implicitly loops over the List/Seq you supply, tests each element of the List with the function you supply. Your function must return true or false, and filter returns the list elements where your function returns true.
Note: Even though I use a List in these examples, the
filter
method can be used on any Scala sequence, includingArray
,ArrayBuffer
,List
,Vector
,Seq
, etc.
Let's look at a few simple examples. In this first example we filter a small list of numbers so that our resulting list only has numbers that are greater than 2:
scala> val nums = List(5, 1, 4, 3, 2) nums: List[Int] = List(5, 1, 4, 3, 2) scala> nums.filter(_ > 2) res0: List[Int] = List(5, 4, 3)
Note that in the real world you'd assign the filtered results to a new List
, like this:
val originalList = List(5, 1, 4, 3, 2) val newList = originalList.filter(_ > 2)
This example shows how to get the even numbers from a List
using a simple modulus test:
scala> nums.filter(_ % 2 == 0) res21: List[Int] = List(4, 2)
You can take that example a step further by filtering and then sorting the list:
scala> nums.filter(_ % 2 == 0).sort(_ < _) warning: there were 1 deprecation warnings; re-run with -deprecation for details res22: List[Int] = List(2, 4)
Tip: You can think of
filter
as meaning retain.
filter method examples with a List of Strings
Here are two filter method examples with a list of Strings:
val fruits = List("orange", "peach", "apple", "banana") scala> fruits.filter(_.length > 5) res21: List[java.lang.String] = List(banana, orange) scala> fruits.filter(_.startsWith("a")) res22: List[java.lang.String] = List(apple)
Combining filter, sort, and map
From the excellent book, Beginning Scala, here's a nice combination of the List
filter, sort, and map methods:
trait Person { def first: String def age: Int def valid: Boolean } Returns the first name of 'valid' persons, sorted by age def validByAge(in: List[Person]) = in.filter(_.valid) .sort(_.age < _.age) .map(_.first)
The following example shows how you can use filter
with map
to transform the type of data that the expression returns. In this case we'll start with a sequence of Person
objects, and transform it into a sequence of String
objects.
We'll start with a simple case class:
scala> case class Person(first: String, last: String, mi: String) defined class Person
Next, we'll create a little sequence of Person
objects:
scala> val fred = Person("Fred", "Flintstone", "J") fred: Person = Person(Fred,Flintstone,J) scala> val wilma = Person("Wilma", "Flintstone", "A") wilma: Person = Person(Wilma,Flintstone,A) scala> val barney = Person("Barney", "Rubble", "J") barney: Person = Person(Barney,Rubble,J) scala> val betty = Person("Betty", "Rubble", "A") betty: Person = Person(Betty,Rubble,A) scala> val peeps = Seq(fred, wilma, barney, betty) peeps: Seq[Person] = List(Person(Fred,Flintstone,J), Person(Wilma,Flintstone,A), Person(Barney,Rubble,J), Person(Betty,Rubble,A))
Finally, we'll combine filter
and map
to get a list of all first names where the last name is "Flintstone":
scala> peeps.filter(_.last == "Flintstone").map(_.first) res0: Seq[String] = List(Fred, Wilma)
The way this works is:
- The
filter
method returns a sequence ofPerson
objects where the last name is "Flintstone". - The
map
method call gets the first name of eachPerson
object. This results in a sequence of strings, where each string is the first name of each person that came out of thefilter
call.
I initially wrote this as a for/yield loop, but then realized I could write this much more concisely with this approach. At the moment I find the for/yield loop to be more readable, and this to be much more concise.
In my opinion, this code can be made a little more readable by using a variable name in the map
expression, as a reminder that you're still dealing with Person
objects:
scala> peeps.filter(_.last == "Flintstone").map(person => person.first) res1: Seq[String] = List(Fred, Wilma)
Scala List filter method summary
I hope these filter
method examples have been helpful. Here's a quick summary of how the filter
method works:
filter
implicitly loops over aList
.filter
takes a function as an argument. That function should take oneList
element as input, perform the test you define, and then return either true or false (aBoolean
).filter
only returnsList
elements that match the filtering expression.