One of the advantage of Scala vs Java is the number of methods available on the collections (List
, Seq
, Buffer
, etc), working with a function programming approach and simplifying the code. Despite Java already has its Stream API, and it does a great job, there are some methods in Scala that makes our work easier.
That’s the case of collect
, which is very similar to the map
, because both use a Function that gets one parameter and returns another one. So, they are used to transform our collection/stream.
Let’s see and example:
case class MyObject(name: String, price: Double) val myList = List(MyObject("foo",42.0), MyObject("bar", 30.0), MyObject("snafu", 100.0))
Whichever of the following lines of code will have the same output.
myList.map(_.name)
myList.map { case _ => _.name }
myList.collect { case _ => _.name }
The output for the above examples is: List("foo", "bar", "snafu")
At this point, the collect
and the map
are very similar. However, with the map
if there are N elements as input, then, there are N elements as output, and that’s the difference of the collect
because for N
input elements there might be M
elements as output.
That’s because with the collect
the elements which don’t match the indicated criteria in the case
can be discriminated or filtered. So, we can do something like:
myList.collect { case _ if (_.price > 50) => _.name }
On that case the output is List("snafu")
. Only one element matches that criteria, therefore from an input of 3 MyObject
elements, there is an output of only 1 String
element.
Let’s see more examples:
val myList = List("foo", "bar", "text", false, 42.0, 100, None, "snafu", Option("maybe")) myList.collect{ case _ : String => _ } // Returns a List("foo","bar","text","snafu") myList.collect { case _ : String if (_.length > 4) => _ } //Returns only the strings with length greater than 4 => List("snafu") myList.collect { case _ : Option if (_.isDefined ) => _.get } //Returns only the records of type Option and with a defined value => List("maybe")
So, the same can be done with a filter
and then a map
.
val myList = List(Option("maybe"), None, Option("foo")) //THESE 2 operations filter and map myList.filter(_.isDefined) .map(_.get) //VS only operation myList.collect { case Some(x) => x } //result List("maybe", "foo")
BUT, it requires two operations. That’s why the collect
can help us to simplify the code. IMHO, I prefer using the collect
.