ORDENACIÓN
Otra de las
funcionalidades básicas de cualquier consulta es la ordenación de los
datos. Por norma nos bastará con dos criterios: campo por el que
filtrar y orden en el que filtrar (ascendente o descendente).
La cláusula
LINQ para realizar el filtrado es orderby. Si no se añade ningún parámetro
de orden, los campos se recuperarán por defecto de forma ascendente. En caso de
querer un orden descendente, añadiremos la palabra reservada descending a
continuación del campo por el que queremos ordenar. La palabra reservada ascending fuerza
a que la ordenación sea, en todo caso, ascendente.
1 2 3 4 5 6 7 8 9 |
var consulta = from cliente in DataLists.ListaClientes orderby cliente.Nombre ascending select cliente; foreach (var cliente in consulta) { Console.WriteLine(string.Format("ID:
{0}\tNOMBRE: {1}\tF.NACIMIENTO: {2}", cliente.Id,
cliente.Nombre, cliente.FechaNac)); } |
En este caso, los registros se ordenan por orden alfabético por el campo nombre, siempre de menor a mayor.
En el caso
contrario (descending) el campo de ordenación es el mismo, pero en orden
descendente (de mayor a menor).
1 2 3 4 5 6 7 8 9 |
var consulta = from cliente in DataLists.ListaClientes orderby cliente.Nombre descending select cliente; foreach (var cliente in consulta) { Console.WriteLine(string.Format("ID:
{0}\tNOMBRE: {1}\tF.NACIMIENTO: {2}", cliente.Id,
cliente.Nombre, cliente.FechaNac)); } |
Ordenación y expresiones lambda
Al igual que
con otros métodos, LINQ permite la utilización de extensiones para permitir el
uso expresiones lambda. Los métodos para este menester son OrderBy y OrderByDescending.
Las consultas equivalentes a las anteriores serían las siguientes:
1 2 |
var consultaAsc =
DataLists.ListaClientes.OrderBy(cliente => cliente.Nombre); var consultaDesc =
DataLists.ListaClientes.OrderByDescending(cliente => cliente.Nombre); |
Además, es
posible invertir el orden en el que una lista está construida mediante el
método Reverse().
1 2 3 4 5 6 7 8 9 |
var consulta = (from cliente in DataLists.ListaClientes orderby cliente.Nombre descending select cliente).Reverse(); foreach (var cliente in consulta) { Console.WriteLine(string.Format("ID:
{0}\tNOMBRE: {1}\tF.NACIMIENTO: {2}", cliente.Id,
cliente.Nombre, cliente.FechaNac)); } |
Como podemos
observar, el resultado es el mismo que utilizar ascending en lugar de
usar descending.
Ordenación múltiple
Es también
posible indicar un suborden dentro de un orden. Por ejemplo, imaginemos que
queremos ordenar todas las líneas de pedido por ID del pedido. Si obtenemos
(por ejemplo) cinco registros cuyo ID de pedido es «1», puede que nos interese
añadir un nuevo campo de ordenación para crear un orden dentro del orden.
Por ejemplo, por el ID del producto. Si es lo que deseamos, podemos hacerlo
separando los campos de ordenación a través de una coma:
1 2 3 |
var consulta = from linea in DataLists.ListaLineasPedido orderby linea.IdPedido, linea.IdProducto select linea; |
El resultado
muestra cómo los registros se ordenan primero por el campo IdPedido y, a
continuación, por IdProducto.
Pero… ¿y si
quisiéramos que la lista se ordenara de forma descendente por el ID de pedido y
a continuación de forma ascendente por el Id de producto? No hay problema. Es
posible utilizar los calificadores ascending y descending junto
al campo que califican, de la siguiente forma:
1 2 3 |
var consulta = from linea in DataLists.ListaLineasPedido orderby linea.IdPedido descending,
linea.IdProducto ascending select linea; |
Nuevamente, el
resultado nos muestra que los pedidos se ordenan del 12 al 1, mientras que los
productos, dentro del orden anterior, se ordenan de menor a mayor.
El equivalente
con expresiones lambda a estas dos funciones sería el siguiente:
1 2 |
var consultaAscAsc =
DataLists.ListaLineasPedido.OrderBy(linea => linea.IdPedido).OrderBy(linea
=> linea.IdProducto); var consultaDescAsc =
DataLists.ListaLineasPedido.OrderByDescending(linea =>
linea.IdPedido).OrderBy(linea => linea.IdProducto); |
Ordenación y paginación
En el artículo
en el que hablamos de los métodos de particionado hablamos acerca de la
paginación de una consulta. A la hora de particionar con el objetivo de paginar
una consulta, hay que tener especial cuidado con el orden en el que se aplican
ambas operaciones: la ordenación siempre se ejecutará antes de la
partición.
Siempre hay que
realizar en primer lugar la operación de ordenación. A continuación, se
paginará (particionará) sobre esa consulta. Así, la siguiente paginación a la
que se le ha aplicado un criterio de ordenación sería correcta:
1 2 3 4 5 |
int tamPagina = 5; int paginaActual = 2; var consultaPaginada = (from producto in DataLists.ListaProductos orderby producto.Descripcion select producto).Skip((paginaActual - 1)
* tamPagina).Take(tamPagina); |
Vemos el resultado:
En cambio, si
realizásemos primero la partición y después la ordenación, lo que estaríamos
logrando sería recuperar n registros sin ordenar que, una vez
recuperados, serían ordenados. Veamos qué ocurriría si invirtiésemos el orden
de la consulta anterior:
1 2 3 4 5 6 7 |
int tamPagina = 5; int paginaActual = 2; var consultaPaginada = (from producto in DataLists.ListaProductos select producto). Skip((paginaActual
- 1) * tamPagina). Take(tamPagina). OrderBy(prod
=> prod.Descripcion); |
Lo cual sería claramente
incorrecto, ya que se aplicaría la ordenación sobre el fragmento de información
que acabamos de recuperar:
Con esto finalizamos las
operaciones de ordenación en LINQ. Mañana echaremos un vistazo a otra de las
características de LINQ: operaciones sobre conjuntos.