Llegamos al grupo de operadores destinados a realizar el
trabajo de ordenación en nuestras colecciones. Dentro de este apartado nos
encontramos con un nuevo actor de este teatro, un actor que tiene una
importancia mínima pero que forma parte de todo este tinglao, es la interfaz
IOrderedEnumerable<TElement> .
Es una interfaz super simple, que hereda de
IEnumerable<TElement>
y de
IEnumerable,
y que simplemente añade un método
CreateOrderedEnumerable,
cuya función será la de crear las ordenaciones. Es importante nombrarla ya que
será el tipo de devolución que generen nuestros operadores de ordenación
OrderBy,
OrderByDescending,
ThenBy
y
ThenByDescending.
Llegados a este momento del repaso de LinQ, volveremos a ver las diferencias para estos operadores
entre la sintaxis de consultas
y nuestras queridas Lambdas.
Recuerda que aquí tienes el indice de todos los posts del Curso de LinQ.
Clase Utilizada para los ejemplos
public class MiClase
{
public string ID { get; set; }
public int Propiedad1 { get; set; }
public decimal Propiedad2 { get; set; }
public static IEnumerable<MiClase> GenerarDatos()
{
return new List<MiClase>()
{
new MiClase { ID = "1", Propiedad1 = 100, Propiedad2 = 100.99m },
new MiClase { ID = "2", Propiedad1 = 20, Propiedad2 = 200.99m },
new MiClase { ID = "3", Propiedad1 = 20, Propiedad2 = 300.99m },
new MiClase { ID = "4", Propiedad1 = 40, Propiedad2 = 400.99m }
};
}
}
OrderBy
El operador
OrderBy
recibe una colección y la devuelve ordenada
(de manera ascendente) por una clave o criterio de ordenación. Esta
clave puede está compuesta por uno o por varios campos. Este operador tiene una
sobrecarga en el que se le puede
añadir un objeto de tipo
IComparer<TEntity>
para elegir el modo de comparación. Algo muy similar al uso de
IEqualityComparer<TEntity>.
Vamos con
sus firmas:
public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector);
public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer);
public static IOrderedEnumerable<TSource> OrderByDescending<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector);
public static IOrderedEnumerable<TSource> OrderByDescending<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer);
El ejemplo de la primera sobrecarga es realmente sencillo:
class Program
{
static void Main(string[] args)
{
var datos = MiClase.GenerarDatos();
var datosOrdenados = datos.OrderBy(a => a.Propiedad1);
foreach (var dato in datosOrdenados)
{
Console.WriteLine($"{nameof(dato.ID)}:{dato.ID} - {nameof(dato.Propiedad1)}:{dato.Propiedad1}");
}
Console.Read();
}
}
Ninguna dificultad, indicamos el campo por el que queremos
ordenar, mediante un selector Func<TSource,
TKey> y poca historia más.
La segunda sobrecarga, nos permitirá añadir un parámetro más
de tipo IComparer<TEntity>
, con el que podremos especificar una
forma propia de ordenación.
En este
Link
podemos ver más información acerca del uso y explicación sobre
IComparer<TEntity> . No vamos a entrar a ver ejemplos de esta sobrecarga, ya que su uso es muy
reducido. El modo de empleo como hemos comentado antes sería calcado a
cualquier ejemplo que admita un
IEqualityComparer<TEntity>.
OrderByDescending
Estamos ante un caso textualmente clavado al anterior,
cambiando el orden de ordenación, descendente en este caso, como su propio
nombre indica.
static void Main(string[] args)
{
var datos = MiClase.GenerarDatos();
var datosOrdenados = datos.OrderByDescending(a => a.Propiedad1);
foreach (var dato in datosOrdenados)
{
Console.WriteLine($"{nameof(dato.ID)}:{dato.ID} - {nameof(dato.Propiedad1)}:{dato.Propiedad1}");
}
Console.Read();
}
ThenBy
El operador ThenBy, permite hacer una sub-ordenación, a una
secuencia ya ordenada, ósea a un IOrderedEnumerable<TElement>
. ThenBy, mantendrá la ordenación
inicial, agregando una segunda catalogación.
Sus firmas son muy parecidas a las de
OrderBy con la disparidad de que el primer parámetro del
método extensor es de tipo
IOrderedEnumerable<TElement>
, en decremento de
IEnumerable<TElement>
.
public static IOrderedEnumerable<TSource> ThenBy<TSource, TKey>(this IOrderedEnumerable<TSource> source, Func<TSource, TKey> keySelector);
public static IOrderedEnumerable<TSource> ThenBy<TSource, TKey>(this IOrderedEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer);
public static IOrderedEnumerable<TSource> ThenByDescending<TSource, TKey>(this IOrderedEnumerable<TSource> source, Func<TSource, TKey> keySelector);
public static IOrderedEnumerable<TSource> ThenByDescending<TSource, TKey>(this IOrderedEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer);
Ejemplo:
static void Main(string[] args)
{
var datos = MiClase.GenerarDatos();
var datosOrdenados = datos.OrderBy(a => a.Propiedad1).ThenByDescending(b => b.ID);
foreach (var dato in datosOrdenados)
{
Console.WriteLine($"{nameof(dato.ID)}:{dato.ID} - {nameof(dato.Propiedad1)}:{dato.Propiedad1}");
}
Console.Read();
}
Se puede decir que ThenBy, nos brinda la posibilidad de hacer
ordenaciones, por más de un campo.
NOTA
No se puede realizar 2 OrderBy seguidos para ordenar por 2
campos o por n campos, porque el desenlace no sería el que esperamos, y es que
el único criterio de ordenación que se mantendría, sería el de la última
llamada, ya que cada una de las ejecuciones del método OrderBy, sobrescribiría
la anterior.
Si repetimos el ejemplo anterior sustituyendo el ThenBy, por otro OrderBy:
static void Main(string[] args)
{
var datos = MiClase.GenerarDatos();
var datosOrdenados = datos.OrderBy(a => a.Propiedad1).OrderBy(b => b.ID);
foreach (var dato in datosOrdenados)
{
Console.WriteLine($"{nameof(dato.ID)}:{dato.ID} - {nameof(dato.Propiedad1)}:{dato.Propiedad1}");
}
Console.Read();
}
El resultado solo tendría en cuenta la ordenación por ID, obviando complemente la
ordenación por Propiedad1 de la
llamada anterior.
Ordenación con Sintaxis de Consulta
El azúcar
sintáctico de LinQ,
nos ofrece una ayuda, fusionando los
conceptos de OrderBy, OrderByDescending, ThenBy y ThenByDescending, dando sentido a su existencia, concibiendo
una sintaxis muy parecida a la de Sql.
Así:
static void Main(string[] args)
{
var datos = MiClase.GenerarDatos();
var datosOrdenados = from dato in datos
orderby dato.Propiedad1, datoID descending
select dato;
foreach (var dato in datosOrdenados)
{
Console.WriteLine($"{nameof(dato.ID)}:{dato.ID} - {nameof(dato.Propiedad1)}:{dato.Propiedad1}");
}
Console.Read();
}
Reverse
El operador Reverse, es otro de esos operadores simples de
toda la biblioteca de métodos extensores de LinQ, su faena radica en generar
una secuencia de salida de tipo IEnumerable<T> del mismo tipo que su
secuencia de entrada, pero en orden inverso.
Aclarar que no realiza ninguna ordenación, ya que mantiene
la secuencia inicial, pero al contrario de su orden:
Tan fácil como si tenemos
0,
1, 2, 3, 4, 5, 6 ,7, 8, 9 à REVERSE
à 9, 8, 7, 6, 5, 4, 3, 2, 1
Vamos a ver su firma:
public static IEnumerable<TSource> Reverse<TSource>(this IEnumerable<TSource> source);
Más simple no puede ser, colección de entrada y genera
colección de salida.
Ejemplo:
static void Main(string[] args)
{
var datos = MiClase.GenerarDatos();
PintarColeccion(datos, "Datos Sin Ordenar -------");
var datosReverse = datos.Reverse();
PintarColeccion(datosReverse, "Datos Sin Ordenados -------");
Console.Read();
}
public static void PintarColeccion(IEnumerable<MiClase> source, string mensajeInicial = null)
{
if ( ! string.IsNullOrWhiteSpace(mensajeInicial)) Console.WriteLine(mensajeInicial);
foreach (var dato in source)
{
Console.WriteLine($"{nameof(dato.ID)}:{dato.ID} - {nameof(dato.Propiedad1)}:{dato.Propiedad1}");
}
}
No hay comentarios :
Publicar un comentario