Aún más LINQ con MoreLinq
Tenía ya rato sin hablar de LINQ, y es que es un tema tan grande que merecería hasta su propio blog... ahora, imagínate si existiera aún más operaciones que se pudieran realizar usando estas herramientas.
Este es el caso de morelinq, un paquete de NuGet que un grupo de entusiastas de C# desarrollan. Este paquete, como su nombre pretende indicar, añade métodos de extensión las colecciones que usamos dentro de nuestro código, permitiéndonos llevar a cabo muchas más operaciones que con LINQ convencional, podríamos hacer pero usando más código.
Vamos a ver unos ejemplos (y vamos a trabajar con dinosaurios):
AtLeast
y AtMost
O, en español: al menos y como máximo. Este par de métodos nos ayudan a responder estas preguntas:
- "¿La colección A contiene al menos X elementos que cumplen con Y condición?"
- "¿La colección A contiene como máximo X elementos que cumplen con Y condición?"
Por ejemplo, primero queremos saber si al menos 5 nombres de dinosaurios terminan en "saurus", y después queremos saber si existe máximo un dinosaurio cuyo nombre comience con "H":
var atLeastFive = dinos.Where(dino => dino.Name.EndsWith("saurus"))
.AtLeast(5);
var atMostOne = dinos.Where(dino => dino.Name.StartsWith("H"))
.AtMost(1);
CountBy
y DistinctBy
El primer método nos ayudará a contar las ocurrencias de determinada propiedad dentro de los objetos de nuestra colección, por ejemplo, si quisiéramos saber la ocurrencia tiene cada altura de dinosaurios en nuestra base de datos podríamos hacer algo como esto:
var countByHeight = dinos.CountBy(d => d.Height);
Que devuelve un diccionario donde cada una de sus entradas es Valor, Ocurrencia
El segundo método nos permite establecer una propiedad con la cual diferenciaremos a los elementos. Esta propiedad nos servirá para discriminar elementos que tengan el valor de dicha propiedad repetida. Este método es útil cuando estamos buscando colecciones sin elementos repetidos. Por ejemplo, en este caso estamos buscando dinosaurios sin altura repetida:
var distinctByHeight = dinos.DistinctBy(d => d.Height);
FallbackIfEmpty
Este método me recuerda un poco al operador null-coalescing, ya que realiza una tarea similar: comprueba si la colección está vacía, y si lo está, devuelve el valor que le indicamos como argumento. Por ejemplo, si buscamos un dinosaurio cuyo nombre comience con Y, y si no existe que nos regrese una colección con un solo elemento:
var dinosaursWithY = dinos.Where(d => d.Name.StartsWith("Y")).FallbackIfEmpty(new Dinosaur[]
{
new Dinosaur { Name = "Noexistesaurus" }
});
MaxBy
y MinBy
Honestamente cuando por primera vez usé LINQ, esperaba que estos métodos existieran, pero para mi sorpresa no era así. Si bien tenemos los métodos Max
y Min
, estos no realizan la misma tarea. MaxBy
y MinBy
permiten indicar el criterio mediante el cual queremos diferenciar a los elementos de nuestra colección. Por ejemplo, para obtener al dinosaurio más alto y al más ligero:
var tallestDinosaur = dinos.MaxBy(d => d.Height);
var lightestDinosaur = dinos.MinBy(d => d.Weight);
TakeLast
y SkipLast
Si ya estás familiarizado con los métodos Take
y Skip
no hay mucho que entender aquí. Estos métodos nos ayudan a seleccionar los últimos n elementos de una colección o a saltárnoslos. Por ejemplo, para elegir los últimos dos y elegir todos menos los últimos dos:
var ultimosDos = dinos.TakeLast(2);
var todosMenosUltimosDos = dinos.SkipLast(2);
TakeUntil
y SkipUntil
De manera similar a Take
y Until
del LINQ regular, estos métodos realizan la tarea de "tomar" o "saltarse" objetos en una colección, con la diferencia de que en este caso no se indica una cantidad específica de elementos, sino que se emplea una condición para determinar hasta qué punto de colección se seleccionarán los elementos que contiene. Por ejemplo, para seleccionar todos los dinosaurios hasta que aparezca uno cuya altura sea mayor o igual a 5 metros:
var dinosNoTanAltos = sortedByHeight.TakeUntil(d => d.Height >= 5);
O para descartar todos los dinosaurios hasta que aparezca uno cuyo peso sea mayor a 6000 kilogramos:
var dinosNoTanLigeros = sortedByWeight.SkipUntil(d => d.Weight >= 6000);
En conclusión
Podrás pensar que este paquete no es la gran cosa (en este post únicamente hablé de cosas sencillas) ya que es fácil implementar nosotros mismos las funcionalidades que este ofrece, sin embargo, ponte a pensar en lo tedioso y propenso a errores que eso puede resultar. Mejor haz uso de esta pequeña biblioteca que te podrá sacar de algun apuro más de una vez.
Instalación
Para instalarlo basta con instalar el paquete de NuGet morelinq.
PM> Install-Package morelinq
Si deseas ver el maravilloso código detrás de este paquete puedes visitar el proyecto en GitHub. Tengo que decirlo, el de MoreLINQ es uno de los códigos mejores estructurados y más entendibles que he visto en mucho, mucho tiempo.