Seguimos con los operadores de LinQ, ahora le toca el turno a los ‘Set Operators’ u Operadores
de Conjunto.
Los operadores de conjunto, nos permiten realizar
actuaciones sobre dos secuencias (
IEnumerable<T>)
del mismo tipo de datos, dando como resultado una colección de este mismo tipo.
Todas y cada una de sus ejecuciones se realizan de forma
diferida
o perezosa.
En caso de que alguna de estas colecciones sea null en el
momento de realizar la llamada, el compilador lanzará un ArgumentNullException.
Vamos a detallar cada uno de sus operadores, para intentar
que todo esto quede un poco más claro, haré un par de ejemplos para cada
operador, uno con datos simples, colecciones de tipo
int, y otro con una de las clases más utilizada en nuestros
ejemplos y que tiene su definición en los post anteriores, el tipo
Automovil. El objetivo es
comprender lo imprescindible, que es el uso de las sobrecargas que admiten un
comparador de tipo
IEQualityComparer<T> para los tipos compuestos, en el que indicaremos al
compilador como queremos que se diferencien 2 objetos de este tipo.
Como en todas las reglas, hay una excepción y esta no iba a
ser menos. El operador Distinct,
cambiará, para utilizar una única colección en decremento de dos, pero eso
lo contaremos un poquito más adelante.
Recuerda que aquí tienes el indice de todos los posts del Curso de LinQ.
Concat Operator
El operador Concat,
enlaza dos conjuntos de elementos, para construir una colección de resultado
con todos y cada uno de los elementos de las dos colecciones. Su homónimo en
SQL, sería el operador UnionAll.
En este caso y dado la simplicidad del método extensor, voy
a poner el código real de la implementación del mismo y no simplemente la
firma:
public static IEnumerable<TSource> Concat<TSource>(IEnumerable<TSource> first, IEnumerable<TSource> second)
{
if (first == null) throw Error.ArgumentNull("first");
if (second == null) throw Error.ArgumentNull("second");
foreach (TSource element in first) yield return element;
foreach (TSource element in second) yield return element;
}
Aunque el operador Concant,
no tenga sobrecarga con comparador, voy a incluir también 2 ejemplos para
continuar en concordancia con todos los del post.
Vamos con el ejemplo de datos simple (struct de tipo int):
static void Main(string[] args)
{
var numeros1 = Enumerable.Range(1, 5).ToList();
var numeros2 = Enumerable.Range(3, 5).ToList();
var numerosConcatenados = numeros1.Concat(numeros2).OrderBy(n => n).ToList();
numerosConcatenados.ForEach(n => Console.WriteLine("{0}", n));
Console.Read();
}
Resultado:
Ejemplo con tipo compuesto (class Automovil):
static void Main(string[] args)
{
var coches = new List<Automovil>
{
new Automovil { Marca = "Renault", Modelo = "Clio" , Carburante = TipoCarburante.Gasolina, Color = Colors.Green },
new Automovil { Marca = "Citroen", Modelo = "C3" , Carburante = TipoCarburante.Diesel , Color = Colors.Gray },
new Automovil { Marca = "Renault", Modelo = "Laguna", Carburante = TipoCarburante.Diesel , Color = Colors.Black },
new Automovil { Marca = "Renault", Modelo = "Megane", Carburante = TipoCarburante.Gasolina, Color = Colors.Blue },
new Automovil { Marca = "Citroen", Modelo = "C4" , Carburante = TipoCarburante.Gasolina, Color = Colors.Black }
};
var coches2 = new List<Automovil>
{
new Automovil { Marca = "Renault", Modelo = "Clio" , Carburante = TipoCarburante.Gasolina, Color = Colors.Green },
new Automovil { Marca = "Seat", Modelo = "Ibiza" , Carburante = TipoCarburante.Diesel , Color = Colors.Gray },
new Automovil { Marca = "Fian", Modelo = "Punto" , Carburante = TipoCarburante.Diesel , Color = Colors.Black },
new Automovil { Marca = "Citroen", Modelo = "C4" , Carburante = TipoCarburante.Gasolina, Color = Colors.Coral }
};
var concatenacionCoches = coches.Concat(coches2);
var concatenacionCochesOrdenados = concatenacionCoches.OrderBy(c => c.Marca).ThenBy(c => c.Modelo).ToList();
coches.Clear();
Console.WriteLine("El resultado de la concatenación es:");
foreach (var coche in concatenacionCochesOrdenados)
{
Console.WriteLine("{0, -10} - {1, -6} - {2}", coche.Marca, coche.Modelo, coche.Color.ToString());
}
Console.Read();
}
Resultado:
Como podemos ver, en el resultado aparecen todos y cada uno
de los elementos de ambas colecciones.Lo hemos ordenado por Marca y
por Modelo, mediante OrderBy
y ThenBy, estos
operadores los veremos un poquito más adelante.
Operador Union
El operador Union,
como su propio nombre indica, une dos secuencias en una, pero en este caso no
con todos los elementos de cada una de las colecciones, solo con los
diferentes. Si hay algún elemento repetido en ambas, este solo se añadirá una
única vez.
El operador Union a diferencia de Concat, si que contiene
una segunda sobrecarga que acepta un comparador
por parámetros.
public static IEnumerable<TSource> Union<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second)
public static IEnumerable<TSource> Union<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
Vamos con el ejemplo de datos simple (struct de tipo int):
static void Main(string[] args)
{
var numeros1 = Enumerable.Range(1, 5).ToList();
var numeros2 = Enumerable.Range(3, 5).ToList();
var numerosUnidos = numeros1.Union(numeros2).OrderBy(n => n).ToList();
numerosUnidos.ForEach(n => Console.WriteLine(" {0}", n));
Console.Read();
}
Resultado:
Con el operador Union, decimos adiós a los datos duplicados.
Ejemplo con tipo compuesto (class Automovil):
static void Main(string[] args)
{
var coches = new List<Automovil>
{
new Automovil { Marca = "Renault", Modelo = "Clio" , Carburante = TipoCarburante.Gasolina, Color = Colors.Green },
new Automovil { Marca = "Citroen", Modelo = "C3" , Carburante = TipoCarburante.Diesel , Color = Colors.Gray },
new Automovil { Marca = "Renault", Modelo = "Laguna", Carburante = TipoCarburante.Diesel , Color = Colors.Black },
new Automovil { Marca = "Renault", Modelo = "Megane", Carburante = TipoCarburante.Gasolina, Color = Colors.Blue },
new Automovil { Marca = "Citroen", Modelo = "C4" , Carburante = TipoCarburante.Gasolina, Color = Colors.Black }
};
var coches2 = new List<Automovil>
{
new Automovil { Marca = "Renault", Modelo = "Clio" , Carburante = TipoCarburante.Gasolina, Color = Colors.Green },
new Automovil { Marca = "Seat" , Modelo = "Ibiza", Carburante = TipoCarburante.Diesel , Color = Colors.Gray },
new Automovil { Marca = "Fian" , Modelo = "Punto", Carburante = TipoCarburante.Diesel , Color = Colors.Black },
new Automovil { Marca = "Citroen", Modelo = "C4" , Carburante = TipoCarburante.Gasolina, Color = Colors.Coral }
};
var unionCoches = coches.Union(coches2, new AutomovilEqualityComparer());
var unionCochesOrdenados = unionCoches.OrderBy(c => c.Marca).ThenBy(c => c.Modelo).ToList();
Console.WriteLine("El resultado de la concatenación es:");
foreach (var coche in unionCochesOrdenados)
{
Console.WriteLine("{0, -10} - {1, -6} - {2}", coche.Marca, coche.Modelo, coche.Color.ToString());
}
Console.Read();
}
Esta es la definición del IEqualityComparer<Automovil>:
public class AutomovilEqualityComparer : IEqualityComparer<Automovil>
{
#region IEqualityComparer Members
public bool Equals(Automovil x, Automovil y)
{
return x.Marca == y.Marca &&
x.Modelo == y.Modelo;
}
public int GetHashCode(Automovil obj)
{
return obj.Marca.GetHashCode() +
obj.Modelo.GetHashCode();
}
#endregion
}
Resultado:
Como podemos observar hemos utilizado un comparador, AutomovilEqualityComparer. En caso
de haber utilizado la primera sobrecarga, el resultado hubiera sido el mismo
que si hubiéramos utilizado el operador Concat,
ya que como hemos dicho en otras ocasiones, el comparer por defecto en las clases compara referencias, y por supuesto todas son diferentes.
El resultado es el mismo que el del operador Union de SQL.
Operador Intersect
Este operador produce una secuencia de salida con los ítems
que son comunes en ambas colecciones.
Este operador también contiene una segunda sobrecarga con su
comparador, para hacer un uso personalizado del mismo.
Estas son
sus firmas:
public static IEnumerable<TSource> Intersect<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second)
public static IEnumerable<TSource> Intersect<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
Vamos con el ejemplo de datos simple (struct de tipo int):
static void Main(string[] args)
{
var numeros1 = Enumerable.Range(1, 5).ToList();
var numeros2 = Enumerable.Range(3, 5).ToList();
var intersectNumeros = numeros1.Intersect(numeros2).OrderBy(n => n).ToList();
intersectNumeros.ForEach(n => Console.WriteLine(" {0}", n));
Console.Read();
}
Resultado:
El resultado está compuesto por los datos comunes de las dos
colecciones.
Ejemplo con tipo compuesto (class Automovil):
static void Main(string[] args)
{
var coches = new List<Automovil>
{
new Automovil { Marca = "Renault", Modelo = "Clio" , Carburante = TipoCarburante.Gasolina, Color = Colors.Green },
new Automovil { Marca = "Citroen", Modelo = "C3" , Carburante = TipoCarburante.Diesel , Color = Colors.Gray },
new Automovil { Marca = "Renault", Modelo = "Laguna", Carburante = TipoCarburante.Diesel , Color = Colors.Black },
new Automovil { Marca = "Renault", Modelo = "Megane", Carburante = TipoCarburante.Gasolina, Color = Colors.Blue },
new Automovil { Marca = "Citroen", Modelo = "C4" , Carburante = TipoCarburante.Gasolina, Color = Colors.Black }
};
var coches2 = new List<Automovil>
{
new Automovil { Marca = "Renault", Modelo = "Clio" , Carburante = TipoCarburante.Gasolina, Color = Colors.Green },
new Automovil { Marca = "Seat" , Modelo = "Ibiza", Carburante = TipoCarburante.Diesel , Color = Colors.Gray },
new Automovil { Marca = "Fian" , Modelo = "Punto", Carburante = TipoCarburante.Diesel , Color = Colors.Black },
new Automovil { Marca = "Citroen", Modelo = "C4" , Carburante = TipoCarburante.Gasolina, Color = Colors.Coral }
};
var intersectCoches = coches.Intersect(coches2, new AutomovilEqualityComparer());
var intersectCochesOrdenados = intersectCoches.OrderBy(c => c.Marca).ThenBy(c => c.Modelo).ToList();
Console.WriteLine("El resultado de la concatenación es:");
foreach (var coche in intersectCochesOrdenados)
{
Console.WriteLine("{0, -10} - {1, -6} - {2}", coche.Marca, coche.Modelo, coche.Color.ToString());
}
Console.Read();
}
Resultado:
Como en el caso anterior con los números simples, el
resultado está compuesto por los datos que concuerdan en cada una de las
colecciones según el criterio de comparación de su IEqualityComparer<Automovil>.
Operador Except
El operador Except, produce una secuencia de resultado con
los elementos de la primera secuencia, que no se encuentran dentro de la
segunda secuencia.
Este operador también contiene una segunda sobrecarga con su
comparador, para hacer un uso personalizado del mismo.
Estas son
sus firmas:
public static IEnumerable<TSource> Except<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second)
public static IEnumerable<TSource> Except<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
Vamos con el ejemplo de datos simple (struct de tipo int):
static void Main(string[] args)
{
var numeros1 = Enumerable.Range(1, 5).ToList();
var numeros2 = Enumerable.Range(3, 5).ToList();
var exceptNumeros = numeros1.Except(numeros2).OrderBy(n => n).ToList();
exceptNumeros.ForEach(n => Console.WriteLine(" {0}", n));
Console.Read();
}
Resultado:
El resultado está compuesto por los elementos de la primera
colección de números que no aparecen en la segunda.
Ejemplo con tipo compuesto (class Automovil):
static void Main(string[] args)
{
var coches = new List<Automovil>
{
new Automovil { Marca = "Renault", Modelo = "Clio" , Carburante = TipoCarburante.Gasolina, Color = Colors.Green },
new Automovil { Marca = "Citroen", Modelo = "C3" , Carburante = TipoCarburante.Diesel , Color = Colors.Gray },
new Automovil { Marca = "Renault", Modelo = "Laguna", Carburante = TipoCarburante.Diesel , Color = Colors.Black },
new Automovil { Marca = "Renault", Modelo = "Megane", Carburante = TipoCarburante.Gasolina, Color = Colors.Blue },
new Automovil { Marca = "Citroen", Modelo = "C4" , Carburante = TipoCarburante.Gasolina, Color = Colors.Black }
};
var coches2 = new List<Automovil>
{
new Automovil { Marca = "Renault", Modelo = "Clio" , Carburante = TipoCarburante.Gasolina, Color = Colors.Green },
new Automovil { Marca = "Seat" , Modelo = "Ibiza", Carburante = TipoCarburante.Diesel , Color = Colors.Gray },
new Automovil { Marca = "Fian" , Modelo = "Punto", Carburante = TipoCarburante.Diesel , Color = Colors.Black },
new Automovil { Marca = "Citroen", Modelo = "C4" , Carburante = TipoCarburante.Gasolina, Color = Colors.Coral }
};
var exceptCoches = coches.Except(coches2, new AutomovilEqualityComparer());
var exceptCochesOrdenados = exceptCoches.OrderBy(c => c.Marca).ThenBy(c => c.Modelo).ToList();
Console.WriteLine("El resultado de la excepción es:");
foreach (var coche in exceptCochesOrdenados)
{
Console.WriteLine("{0, -10} - {1, -6} - {2}", coche.Marca, coche.Modelo, coche.Color.ToString());
}
Console.Read();
}
Resultado:
Al igual que con el caso simple, tenemos como resultado, los
automóviles del primer grupo que no están en el segundo, siempre baja el
criterio marcado por IEqualityComparer<Automovil>.
Operador Distinct
El operador Distinct, es el caso especial dentro de los ‘Set
Operators’, ya que solo opera sobre una colección de datos. Realiza una
distinción para dar como resultado una secuencia con los distintos elementos de
la colección.
Este operador también contiene una segunda sobrecarga con su
comparador, para hacer un uso personalizado del mismo.
Estas son
sus firmas:
public static IEnumerable<TSource> Distinct<TSource>(this IEnumerable<TSource> source)
public static IEnumerable<TSource> Distinct<TSource>(this IEnumerable<TSource> source, IEqualityComparer<TSource> comparer)
Vamos con el ejemplo de datos simple (struct de tipo int):
static void Main(string[] args)
{
var numeros1 = new int[] { 0, 1, 1, 1} ;
var distinctNumeros = numeros1.Distinct().ToList();
distinctNumeros.ForEach(n => Console.WriteLine(" {0}", n));
Console.Read();
}
Resultado:
El resultado ha eliminado toda la redundancia de elementos
‘1’, y solo ha dejado los diferentes.
Ejemplo con tipo compuesto (class Automovil):
static void Main(string[] args)
{
var coches = new List<Automovil>
{
new Automovil { Marca = "Renault", Modelo = "Clio" , Carburante = TipoCarburante.Gasolina, Color = Colors.Green },
new Automovil { Marca = "Citroen", Modelo = "C3" , Carburante = TipoCarburante.Diesel , Color = Colors.Gray },
new Automovil { Marca = "Renault", Modelo = "Laguna", Carburante = TipoCarburante.Diesel , Color = Colors.Black },
new Automovil { Marca = "Renault", Modelo = "Megane", Carburante = TipoCarburante.Gasolina, Color = Colors.Blue },
new Automovil { Marca = "Citroen", Modelo = "C4" , Carburante = TipoCarburante.Gasolina, Color = Colors.Black },
new Automovil { Marca = "Citroen", Modelo = "C4" , Carburante = TipoCarburante.Gasolina, Color = Colors.White },
new Automovil { Marca = "Citroen", Modelo = "C4" , Carburante = TipoCarburante.Gasolina, Color = Colors.Violet },
new Automovil { Marca = "Citroen", Modelo = "C4" , Carburante = TipoCarburante.Gasolina, Color = Colors.Yellow }
};
var distinctCoches = coches.Distinct(new AutomovilEqualityComparer());
Console.WriteLine("El resultado de los diferentes es:");
foreach (var coche in distinctCoches)
{
Console.WriteLine("{0, -10} - {1, -6} - {2}", coche.Marca, coche.Modelo, coche.Color.ToString());
}
Console.Read();
}
Resultado:
El resultado ha sido el conjunto de los diferentes
automóviles de la colección coches, según el criterio de nuestros IEqualityComparer<Automovil>.
No hay comentarios :
Publicar un comentario