En C# no existen cláusulas específicas para estas operaciones de partición, por tanto debemos usar los métodos extensores definidos por .NET Framework.
Si de una consulta de LINQ queremos tomar solo una parte de la misma, utilizaremos las funciones
Skip y Take. Como argumento de esas funciones indicaremos un número de elementos; la primera se “saltará” esos elementos, mientras que la segunda “tomará” esa cantidad de elementos.
Por ejemplo, en el listado 7.44 vemos cómo usar Skip para saltar los 3 primeros elementos de la colección artículos, es decir, incluirá en el resultado desde el cuarto artículo hasta el final.
var res1 = (from a in artículos select a).Skip(3);
Listado 7.44. Skip se saltará los elementos que le indiquemos
En el listado 7.45 vemos cómo usar Take para tomar solo los tres primeros elementos de la colección.
var res2 = (from a in artículos select a).Take(3);
Listado 7.45. Take tomará el número de elementos que indiquemos a continuación
También podemos utilizar estas dos funciones para que se salte un número de elementos y a partir de esa posición tome los que indiquemos. Por ejemplo, en el listado 7.46 tomamos 2 elementos a partir del tercero (saltamos dos y tomamos dos).
var res3 = (from a in artículos select a).Skip(2).Take(2);
Listado 7.46. Podemos usar Skip y Take en la misma consulta
Como es de suponer, estas funciones las podemos usar tanto en consultas directas (como en estos ejemplos) o bien en consultas que previamente se han asignado a una variable. En el código del listado 7.47 vemos cómo realizar las tres operaciones mostradas anteriormente, pero usando una variable a la que previamente le hemos asignado la consulta en la que filtramos solo los artículos que cumplan cierta condición y estén ordenados por la descripción.
var res = from a in artículos where a.IVA == 16M orderby a.Descripción select a;
var res01 = res.Skip(3);
var res02 = res.Take(3);
var res03 = res.Skip(2).Take(2);
Aunque en los primeros ejemplos de esta sección, al no realizar ningún tipo de filtro, podíamos haber usado directamente la colección artículos (ver listado 7.48), pero lo importante aquí no es saber a qué colección se puede aplicar, si no cómo aplicar estas funciones, que como vemos, en este caso concreto es bien simple.
var res4 = artículos.Skip(2).Take(3);
Listado 7.48. Las funciones de partición las podemos aplicar a cualquier colección de datos
Estas dos funciones las podemos usar para realizar paginaciones de datos, por ejemplo, en el listado 7.49 vemos cómo utilizar estas instrucciones para ir mostrando de 5 en 5 las facturas que tenemos. En cada repetición del bucle usamos un valor para indicar cuantos elementos debemos saltar y lo indica- remos después de Skip. Inicialmente saltamos cero elementos, pero después vamos saltando los que vayamos acumulando, es decir, los que ya hemos mostrado. Por otro lado, Take siempre tomará el número que queremos mostrar en la página. Como es de suponer, si quedan menos artículos que los indicados en Take, solo se devolverán los que resten a partir de la posición indicada por Skip.
var actual = 0;
var total = 5;
var pg = 0;
while(true) {
var res1 = (from f in facturas
select f).Skip(actual).Take(total);
// Si no hay datos, salir
if(res1.Count() == 0) break;
Console.WriteLine("Página {0}", ++pg); foreach(var f in res1)
{
Console.WriteLine("{0}, {1} {2:dd/MM/yyyy}", f.Numero, f.Cliente.Empresa, f.Fecha); }
Console.WriteLine();
actual += total; };
Listado 7.49. Podemos usar Skip y Take para realizar paginaciones
SkipWhile y TakeWhile
Además de estas dos funciones, existen otras dos en las que podemos indicar una condición para saltar o tomar los elementos mientras se cumpla dicha condición (en Visual Basic sería equivalente a añadir a continuación de las cláusulas la instrucción While, pero como ya he comentado, en C# no existen como instrucciones propias del lenguaje, por tanto, debemos usar métodos extensores para realizar estas operaciones).
Cuando utilizamos SkipWhile o TakeWhile se realizarán las mismas operaciones saltar o tomar, pero en lugar de indicar un número de elementos, éstos se evaluarán según la condición que pongamos en la expresión lambda que indicaremos como argumento de las funciones. Pero debemos tener en cuenta que en cuanto la condición deje de cumplirse, se incluirán los elementos que queden. Para entenderlo mejor, debemos tener en cuenta que se hace algo como: saltar (o tomar) mientras se cumpla la condi- ción, y en cuanto deje de cumplirse, todo lo que haya también se incluye.
Por ejemplo, si utilizamos el código del listado 7.50, saltaremos los artículos cuyo precio de venta sea superior a 0.9; en cuanto se encuentre uno que no cumple esa condición, se incluirá ese artículo y todos los que sigan (independientemente de que se cumpla o no la condición).
var res1 = (from a in artículos
select a).SkipWhile(a => a.PrecioVenta > 0.9M);
Listado 7.50. SkipWhile se saltará los elementos que cumplan la condición hasta que se encuentre uno que no la cumpla
Si realmente quisiéramos todos los elementos que no tengan un precio mayor de 0.9, podríamos usar la cláusula where, tal como vemos en el listado 7.51.
var res2 = from a in artículos
where a.PrecioVenta <= 0.9M select a;
Listado 7.51. Si realmente queremos todos los elementos que cumplan una condición, es preferible usar where TakeWhile funciona de forma parecida, solo que en lugar de saltarse los elementos que cumplan esa condición, los tomará “mientras” se cumpla; en este caso, hay que comentar lo mismo que con
SkipWhile, ya que cuando se encuentre con un elemento que no cumpla esa condición, se dejará de comprobar y, por tanto, solo se incluirán en la consulta los primeros elementos que cumplan esa con- dición. Aunque en el caso de TakeWhile podemos tener la certeza de que solo se tomarán los primeros elementos que cumplan la condición indicada en la expresión lambda del argumento pasado a la fun- ción.
Por ejemplo, con el código del listado 7.52, la consulta tendrá cero elementos, ya que la condición es “toma los elementos mientras sea mayor de tres” y como resulta que el primer elemento es menor de ese valor, pues, deja de analizar los restantes.
var nums = new[] { 1, 9, 8, 2, 5, 7, 4, 3, 6 };
var res3 = nums.TakeWhile(n => n > 3);
Listado 7.52. TakeWhile solo tomará los que cumplan la condición hasta que se encuentre con un elemento que no la cumple
Por supuesto, podemos combinar Skip y Take con o sin While. Por ejemplo, en el código del listado 7.53, nos saltamos unos cuantos elementos y después ponemos la condición de los que queremos tomar, en este caso en particular, obtendríamos los valores 5, 7 y 4.
var res4 = nums.Skip(4).TakeWhile(n => n > 3);
Listado 7.53. Podemos mezclar Skip y Take con o sin While