Uso de Collect en Scala

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.

Leave a Reply

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