Una de las ventajas de Scala con respecto a Java, es su gran cantidad de métodos para utilizar en colecciones (List
, Seq
, Buffer
, etc), y trabajar con un enfoque funcional, simplificando el código. A pesar que Java tiene su Stream API, el cual hace muy bien su trabajo, hay algunos métodos que se encuentran en Scala y proporciona muchas más facilidades.
Este es el caso de collect
el cual es muy similar a un map
puesto que utiliza una función que recibe un parámetro y retorna otro, el cual se utiliza para transformar nuestro “stream”.
Vamos a ver un ejemplo:
case class MyObject(name: String, price: Double) val myList = List(MyObject("foo",42.0), MyObject("bar", 30.0), MyObject("snafu", 100.0))
Cualquiera de las siguientes lineas de código proporcionan la misma salida.
myList.map(_.name)
myList.map { case _ => _.name }
myList.collect { case _ => _.name }
El resultado sería para los anteriores ejemplos: List("foo", "bar", "snafu")
Hasta este punto tanto collect
como map
funciona muy similar. Sin embargo con el map
si hay N elementos este retornará N elementos como resultado, y esta es la diferencia del collect
ya que para N
elementos de entrada podemos tener M
elementos de salida.
Esto es debido a que con el collect se puede discriminar o filtrar los elementos que no cumplen con los criterios indicados en el case
. De esta manera podemos hacer algo como:
myList.collect { case _ if (_.price > 50) => _.name }
En este caso la salida es List("snafu")
. Un solo elemento cumple con ese criterio, por lo tanto de una entrada de 3 elementos MyObject
se obtuvo como salida un lista de 1 elemento String
.
Veamos otro ejemplo más.
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")
Ahora bien, se puede hacer lo mismo con un filter
y luego un 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")
Pero requiere de 2 operaciones. Es por esto que el collect
nos puede ayudar a simplificar el código y en lo particular es mi opción de preferencia.