Sentencias en LINQ (VII): Operaciones sobre conjuntos


Las listas generadas mediante el álgebra relacional no son, en última instancia, más que conjuntos de datos. Y si nos ceñimos a una definición formal de conjunto, tenemos que se trata de una agrupación de elementos que es, a su vez, un elemento.

Sobre estos conjuntos pueden realizarse una serie de operaciones básicas:

  • Distinción: selección de los elementos del conjunto que no son iguales a ningún otro.
  • Unión: creación de un conjunto a partir de los elementos de dos o más conjuntos, sin tener en cuenta los elementos repetidos.
  • Concatenación: similar a la unión, pero teniendo en cuenta los elementos repetidos.
  • Comparación: comprueba si los conjuntos poseen exactamente los mismos elementos.
  • Intersección: creación de un conjunto a partir de los elementos comunes a dos o más conjuntos.
  • Diferencia: creación de un conjunto a partir de los elementos de un conjunto cuyos elementos NO se encuentran en otro conjunto.

La operación de distinción (Distinct) la hemos visto en artículos anteriores, por lo que nos centraremos en el resto de operaciones. Comenzaremos por realizar tres consultas compuestas. Una de ellas contendrá información sobre las compras realizadas con valor superior a 20 euros. Otra contendrá las compras por valor entre 7 y 22 euros. La tercera contendrá información sobre compras con valor inferior a 10 euros.

Para ello, generaremos una consulta genérica que realice la agrupación y, a partir de esta, tres consultas más que apliquen los filtros deseados a la primera:


            var consulta =  from linea in DataLists.ListaLineasPedido
                            join producto in DataLists.ListaProductos
                            on linea.IdProducto equals producto.Id
                            group new { linea, producto } by linea.IdPedido into grupoLineaProducto
                            select new
                            {
                                IdPedido = grupoLineaProducto.Key,
                                ValorTotal = grupoLineaProducto.Sum(lineaProducto => lineaProducto.linea.Cantidad * lineaProducto.producto.Precio)
                            };

            var consulta1 = consulta.Where(elemento => elemento.ValorTotal > 20);
            var consulta2 = consulta.Where(elemento => ((elemento.ValorTotal > 7) && (elemento.ValorTotal < 22)));
            var consulta3 = consulta.Where(elemento => elemento.ValorTotal < 10);

El resultado de iterar sobre estos tres conjuntos será el siguiente:

Una vez tenemos tres conjuntos (sets) de datos, ya podemos empezar a realizar operaciones con ellos.

Union

La unión de conjuntos crea un conjunto a partir de los elementos de dos o más conjuntos. Se realiza mediante el método Union, que recibirá un conjunto como parámetro.

Así, la unión de los tres conjuntos se realizará mediante el siguiente código:


            var consultaUnion = consulta1.Union(consulta2).Union(consulta3);

El resultado de la operación será el siguiente:

Como podemos observar, la unión de los conjuntos proporciona un nuevo conjunto compuesto por los elementos de los conjuntos ignorando los elementos repetidos.

Por lo tanto ¿cómo podríamos hacer para unir los conjuntos anteriores respetando todos sus elementos? Es decir, ¿cómo permitir elementos repetidos dentro de una unión? Para realizar esta operación no usaremos la unión, sino la concatenación.

Concat

La concatenación realiza una unión completa de dos conjuntos, sin eliminar los elementos duplicados que están presentes en más de un conjunto. Su sintaxis es la siguiente:


            var consultaConcat = consulta1.Concat(consulta2).Concat(consulta3);

Como vemos, el resultado es similar al de la unión, salvo por la presencia de elementos duplicados (pedidos número 2 y 5)

SequenceEqual

Es posible comparar los elementos de una consulta a partir de los elementos y del orden que esta posee, es decir, comprobando que se trata de dos secuencias iguales. Así, las siguientes dos consultas tendrán, teóricamente, los mismos elementos pero en distinto orden, mientras que la tercera consulta tendrá los mismos elementos que la primera:


            var consultaUnion1 = consulta1.Union(consulta2);
            var consultaUnion2 = consulta2.Union(consulta1);
            var consultaUnion3 = consulta1.Union(consulta2).Union(consulta1);

El método SequenceEqual devolverá true si los elementos son iguales y se encuentran en el mismo orden, y devolverá false en caso contrario:


            foreach (var elemento in consultaUnion1)
            {
                Console.WriteLine(string.Format("ID PEDIDO: {0}\tVALOR:{1}",
                    elemento.IdPedido, elemento.ValorTotal));
            }
            Console.WriteLine("--------------------------------------------------");
            foreach (var elemento in consultaUnion2)
            {
                Console.WriteLine(string.Format("ID PEDIDO: {0}\tVALOR:{1}",
                    elemento.IdPedido, elemento.ValorTotal));
            }
            Console.WriteLine("--------------------------------------------------");
            foreach (var elemento in consultaUnion3)
            {
                Console.WriteLine(string.Format("ID PEDIDO: {0}\tVALOR:{1}",
                    elemento.IdPedido, elemento.ValorTotal));
            }
            Console.WriteLine(string.Format("Son iguales los conjuntos 1 y 2? {0}",
                consultaUnion1.SequenceEqual(consultaUnion2) ? "Sí" : "No"));
            Console.WriteLine(string.Format("Son iguales los conjuntos 2 y 3? {0}",
                consultaUnion2.SequenceEqual(consultaUnion3) ? "Sí" : "No"));
            Console.WriteLine(string.Format("Son iguales los conjuntos 1 y 3? {0}",
                consultaUnion1.SequenceEqual(consultaUnion3) ? "Sí" : "No"));

El resultado muestra que sólo las secuencias 1 y 3 son iguales, pese a que los tres conjuntos poseen los mismos elementos:

Intersect

La intersección de dos conjuntos devolverá como resultado un nuevo conjunto con los elementos que se encuentren en ambos conjuntos. Así, si realizamos la intersección de los dos primeros conjuntos:


            var consultaInterseccion1 = consulta1.Intersect(consulta2);
            var consultaInterseccion2 = consulta2.Intersect(consulta3);

Los resultados serán los elementos comunes, es decir:

Como podemos ver, los conjuntos 1 y 2 comparten el pedido 2. Los conjuntos 2 y 3 tienen tan sólo en común el pedido 5. Si realizásemos la intersección entre los conjuntos 1 y 3, el conjunto resultante resultaría vacío, ya que no poseen elementos en común.

Except

La diferencia de dos conjuntos dará como resultado aquellos elementos del primer conjunto (ojo: sólo del primer conjunto) que no se encuentren en el segundo. Así, si el primer conjunto tiene los elementos (1, 2) y el segundo tiene los elementos (2, 3, 4, 5, 6, 7, 8, …, 1000), la diferencia del primero respecto al segundo serán aquellos elementos del primero que no se encuentran en el segundo, es decir (1), ya que el elemento 2 sí que pertenece al segundo conjunto.

A diferencia de la unión y la intersección, esta operación no es conmutativa, por lo que hay que tener cuidado con el orden en el que se aplica. Así, la diferencia entre el primer conjunto y el segundo se calculará mediante el siguiente código:


            var consultaDiferencia1 = consulta1.Except(consulta2);

El cual dará lugar al siguiente resultado:

Como podemos ver, el pedido 2 pertenecía tanto al primer conjunto como al segundo, por lo que el resultado será el primer conjunto excepto ese elemento.

Si decidimos invertir la operación (diferencia entre segundo conjunto y el primero), vemos que el resultado cambia.


            var consultaDiferencia2 = consulta2.Except(consulta1);

Lo cual generará el siguiente resultado:

En este caso, comprobamos que el resultado comprende el Segundo conjunto, al que se le ha eliminado el elemento común (Pedido número 2).

Anuncios

One comment

  1. Hola, muy buen ejemplo..Ahora como solucionás si cada tabla tiene 2 campos claves ?, porque no puedo usar dos veces la palabra “equals”. No he podido encontrar la solución

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s