Using collect in Scala

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.

Leave a Reply

Your email address will not be published. Required fields are marked *