CUANTIFICADORES,
GENERADORES Y CONVERSORES
Hasta ahora hemos visto las
operaciones más comunes que pueden realizarse mediante LINQ. Por lo tanto, sólo
nos queda echarle un vistazo a un subconjunto de sentencias de carácter más
auxiliar, tales como la cuantificación, generación y conversión de elementos.
Any
El método Any toma
como argumento un delegado o expresión lambda que tomará como parámetro un
elemento de la lista y devolverá un valor booleano que indicará si al
menos un elemento de la lista cumple la condición especificada.
Así, si
quisiéramos saber si se ha vendido al menos un producto cuyo ID sea
3, podríamos optar por, bien crear un método que realice la comparación y que
será pasado como delegado, bien utilizar una expresión lambda que realice la
misma operación.
1 2 3 4 5 6 7 8 9 10 |
// OPCIÓN 1: Usamos un delegado de
una función que toma un elemento del listado como parámetro // y devuelve un booleano que
devuelve el resultado de la comparación public bool PedidoConProductoIgualA3(LineaPedido
linea) { return linea.IdProducto == 3; } bool existe =
DataLists.ListaLineasPedido.Any(PedidoConProductoIgualA3); // OPCIÓN 2: Usamos una expresión
lambda que realice la misma operación bool existe =
DataLists.ListaLineasPedido.Any(linea => (linea.IdProducto == 3)); |
Es posible usar
este método también en agrupaciones. Por ejemplo, si quisiéramos recuperar los
pedidos que contengan una línea de pedido asociada al producto con ID=3,
haríamos algo similar a lo anterior, pero utilizando el método Any en
la agrupación.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
var consulta = from linea in DataLists.ListaLineasPedido group linea by linea.IdPedido into grupoPedido where grupoPedido.Any(lineaPedido =>
(lineaPedido.IdProducto == 3)) select new { IdPedido
= grupoPedido.Key, LineasPedido
= grupoPedido }; foreach (var pedido in consulta) { Console.WriteLine(string.Format("Pedido:
{0}", pedido.IdPedido)); foreach (var lineaPedido in pedido.LineasPedido) { Console.WriteLine(string.Format("\tID:
{0}\tPRODUCTO: {1}\tCANTIDAD: {2}", lineaPedido.Id,
lineaPedido.IdProducto, lineaPedido.Cantidad)); } } |
All
Si, por el
contrario, lo que queremos comprobar es si todos los miembros de un
conjunto cumplen una propiedad, haremos uso del método All, cuya sintaxis
es similar al método Any. Así, si quisiéramos comprobar que todas las
líneas de pedido tienen asociado más de un producto, bastaría con codificar lo
siguiente:
1 |
bool resultado =
DataLists.ListaLineasPedido.All(linea => (linea.Cantidad > 0)); |
Range
LINQ también
proporciona un conjunto de métodos que permiten generar datos. Por
ejemplo, Range es capaz de generar una secuencia de datos a partir de
un número inicial y un número de elementos. El siguiente método generará un
listado con mil enteros:
1 2 3 4 5 6 |
var numeros = Enumerable.Range(1,
1000); foreach (int numero in numeros) { Console.Write(string.Format("{0}
", numero)); } |
Repeat
Otro modo de
generar datos es mediante el método Repeat. Éste es capaz de crear un listado
con n copias de un mismo objeto. Por ejemplo:
1 2 3 4 5 6 7 |
Cliente cliente =
DataLists.ListaClientes[0]; var repeticion =
Enumerable.Repeat(cliente, 10); foreach (var c in repeticion) { Console.WriteLine(string.Format("ID:
{0}\tNOMBRE: {1}", c.Id,
c.Nombre)); } |
Esto hará que
generemos un listado compuesto por el objeto cliente repetido 10
veces.
ToArray y ToList
Estos métodos
convierten una secuencia en un array cuyo tipo se corresponde con el tipo
contenido en la propia secuencia y en un objeto de la clase List<T>,
siendo T el tipo contenido en la secuencia. Su uso no es muy
complicado.
Además, la
ejecución de alguno de estos métodos conlleva un efecto colateral: la
ejecución inmediata de la consulta (recordemos que la declaración de la
sentencia LINQ no provoca su ejecución, sino que ésta es ejecutada en el
momento en el que se utiliza).
1 2 3 4 5 |
var consulta = from producto in DataLists.ListaProductos select producto; Producto[] arrayProductos =
consulta.ToArray(); List<Producto>
listaProductos = consulta.ToList(); |
ToDictionary
El método ToDictionary es
capaz de generar un objeto de la clase Dictionary<TKey, TElement>,
en el que el primer elemento se correspondería con la clave y el segundo
elemento, con el objeto en sí. El siguiente ejemplo generará un diccionario en
el que la clave se corresponde con el identificador del producto y el valor,
con el producto en sí. Para ello, nuevamente, haremos uso de expresiones
lambda.
1 2 3 4 5 6 7 |
Dictionary<int, Producto>
diccionarioProductos = consulta.ToDictionary(producto => producto.Id); foreach (int clave in diccionarioProductos.Keys) { Console.WriteLine(string.Format("ID:
{0}\tPRODUCTO:{1}", clave,
diccionarioProductos[clave].Descripcion)); } |
Recordemos que, en un diccionario, la clave tiene que ser única. El resultado del siguiente código será el siguiente:
OfType
Por último, el
método OfType devolverá aquellos elementos del tipo indicado.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// Obtenemos los productos var consultaProductos = from producto in DataLists.ListaProductos select producto; // Obtenemos los clientes var consultaClientes = from cliente in DataLists.ListaClientes select cliente; // Creamos un array que contendrá
productos y clientes var consulta =
((object[])consultaProductos.ToArray()). Union((object[])consultaClientes.ToArray()); // Extraemos los productos del
array anterior var soloProductos =
consulta.OfType<Producto>(); foreach(var elemento in soloProductos) { Console.WriteLine(string.Format("ID:
{0}\tPRODUCTO: {1}", elemento.Id,
elemento.Descripcion)); } |
En el ejemplo
obtenemos dos conjuntos: uno de productos y otro de clientes. A continuación
convertimos el primer conjunto en un array de objetos (object[]) mediante el
método ToArray() y a continuación le añadimos (mediante Union)
los elementos del segundo conjunto, que también convertimos previamente en
array.
A continuación,
mediante OfType(), filtramos aquellos objetos cuyos elementos sean de la
clase Producto.