Using collect in Scala

Advertisements

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.

Advertisements

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.

Advertisements

Leave a Reply

Your email address will not be published.