Operadores de LINQ: Set

Tabla de contenidos:

Distinct

El operador Distinct sirve para eliminar elementos duplicados de una secuencia.

Código necesario para los ejemplos:

Una lista de ciudades (con algunas repetidas):

List<string> ciudades = new List<string>{"Cádiz", "Málaga", "Murcia", "Málaga", "Madrid", "Barcelona",
										 "Galicia", "Bilbao", "Cádiz"};

Un comparador de iniciales:

public class ComparadorInicial : IEqualityComparer<string>
{
	public bool Equals(string x, string y)
	{
		return x[0] == y[0];
	}

	public int GetHashCode(string obj)
	{
		return obj[0].GetHashCode();
	}
}

Volver al operador Distinct

Distinct estándar:

public static IEnumerable<TSource> Distinct<TSource>(
	this IEnumerable<TSource> source
)

El operador Distinct no toma ningún parámetro, solo la secuencia que usará el operador, pero eso es implicito. Devuelve una enumeración donde ya no hay entradas repetidas.

Veamoslo en un ejemplo:

IEnumerable<string> ciudadesUnicas = ciudades.Distinct();

¿Difícil? Claro que no. Como es lógico, la enumeración contiene:

Cádiz
Málaga
Murcia
Madrid
Barcelona
Galicia
Bilbao

Ninguna ciudad repetida :) .

Volver al operador Distinct

Distinct + comparador personalizado:

public static IEnumerable<TSource> Distinct<TSource>(
	this IEnumerable<TSource> source,
	IEqualityComparer<TSource> comparer
)

Ahora tenemos la posibilidad de comparar cada elemento de la secuencia de una forma personalizada.

Por ejemplo, consideraremos una ciudad como repetida cuando comience con la misma letra que otra ya existente. Por ejemplo, si ya está Barcelona que empieza por B, no queremos a Bilbao puesto que empieza por B también.

Para ello usaremos el comparador adjunto:

IEnumerable<string> ciudadesUnicas = ciudades.Distinct(new ComparadorInicial());

El resultado como cabe de esperar es:

Cádiz
Málaga
Barcelona
Galicia

Volver a la tabla de contenidos

Except

El operador Except proporciona la diferencia de conjuntos de dos secuencias.

Código necesario para los ejemplos:

Una lista de ciudades (con algunas repetidas):

List<string> ciudades = new List<string>{"Cádiz", "Málaga", "Murcia", "Málaga", "Madrid", "Barcelona",
										 "Galicia", "Bilbao", "Cádiz"};

Una lista de ciudades a descartar:

List<string> aDescartar = new List<string>{"Galicia", "Murcia"};

Una lista de iniciales a descartar:

List<string> inicialDescartar = new List<string>{"C", "M"};

Un comparador de iniciales:

public class ComparadorInicial : IEqualityComparer<string>
{
	public bool Equals(string x, string y)
	{
		return x[0] == y[0];
	}

	public int GetHashCode(string obj)
	{
		return obj[0].GetHashCode();
	}
}

Volver al operador Except

Except estándar:

public static IEnumerable<TSource> Except<TSource>(
	this IEnumerable<TSource> first,
	IEnumerable<TSource> second
)

El operador Except toma como parámetro otra secuencia del mismo tipo que usará para descartar elementos. Devuelve una enumeración con los elementos no descartados.

Vamos a explicarlo mejor. El operador recorre la primera lista y va descartando los elementos repetidos (tal y como hace Distinct). Una vez ha recorrido la primera secuencia, empieza a recorrer la segunda. Ahora descartará todo elemento que aparezca en las dos secuencias. Veamoslo con un ejemplo:

IEnumerable<string> ciudadesFiltradas = ciudades.Except(aDescartar);

Tenemos una secuencia que contiene varias ciudades. Empieza descartando las repetidas (Como Málaga, Cádiz…). Cuando termina de recorrer la primera secuencia, empieza a recorrer la segunda, la cual contiene el nombre de dos ciudades. Cogerá la primera, Galicia, si existe en la primera secuencia pues la descarta de dicha secuencia, si no, simplementa se ignora. Así con el resto.

Entonces, una vez descartadas las repetidas y descartadas aquellas que coincidan en las dos secuencias, nos quedaría:

Cádiz
Málaga
Madrid
Barcelona
Bilbao

Volver al operador Except

Except + comparador personalizado:

public static IEnumerable<TSource> Except<TSource>(
	this IEnumerable<TSource> first,
	IEnumerable<TSource> second,
	IEqualityComparer<TSource> comparer
)

Ahora tenemos la posibilidad de usar un comparador personalizado a la hora de comprobar qué elementos tenemos que descartar. Veamos un ejemplo y analicemoslo:

IEnumerable<string> ciudadesFiltradas = ciudades.Except(inicialDescartar, new ComparadorInicial());

inicialDescartar contiene una C y una M. Según el comparador que hemos usado, ¿Descartará todas las C y todas las M? No exactamente…

Recordar como trabaja el operador.

Primero recorre la primera secuencia comparando cada elemento usando el comparador proporcionado. Eso hará que cuando una palabra empiece por una letra, las otras que empiecen por dicha letra serán descartadas directamente. Así que cuando encuentra Barcelona ya descartará Bilbao por empezar por B también. Una vez recorre la primera secuencia, empieza con la segunda y ya directamente descarta de la primera secuencia todas las palabras que empiecen por C y por M.

Así que la cosa terminaría así:

Barcelona
Galicia

¿Sorprendido? Claro que no, descartó todas las que empezaban por C y por M además de Bilbao puesto que ya había una que empezara por B (Barcelona).

Volver a la tabla de contenidos

Intersect

El operador Intersect proporciona la intersección de conjuntos de dos secuencias. O lo que es lo mismo, aquellos elementos que coincidan en las dos secuencias.

Código necesario para los ejemplos:

Una lista de ciudades:

List<string> ciudades1 = new List<string>{"Cádiz", "Málaga", "Murcia", "Madrid", "Barcelona",
										 "Galicia", "Bilbao"};

Otra lista de ciudades:

List<string> ciudades2 = new List<string>{"Cádiz", "Málaga", "Bilbao", "Granada", "Pamplona"};

Un comparador de iniciales:

public class ComparadorInicial : IEqualityComparer<string>
{
	public bool Equals(string x, string y)
	{
		return x[0] == y[0];
	}

	public int GetHashCode(string obj)
	{
		return obj[0].GetHashCode();
	}
}

Volver al operador Intersect

Intersect estándar:

public static IEnumerable<TSource> Intersect<TSource>(
	this IEnumerable<TSource> first,
	IEnumerable<TSource> second
)

Intersect toma como parámetro una secuencia que se usará a la hora de hacer la intersección. Devuelve una enumeración con aquellos elementos que coincidan en ambas secuencias.

Por ejemplo, si queremos crear una enumeración con las ciudades en común de las dos listas de ciudades que tenemos:

IEnumerable<string> ciudades = ciudades1.Intersect(ciudades2);

Nada dificil, el resultado es:

Cádiz
Málaga
Bilbao

Esas tres ciudades coinciden en ambas listas :)

Volver al operador Intersect

Intersect + comparador personalizado:

public static IEnumerable<TSource> Intersect<TSource>(
	this IEnumerable<TSource> first,
	IEnumerable<TSource> second,
	IEqualityComparer<TSource> comparer
)

Como viene siendo costumbre, además de nuestra secuencia para la intersección, podemos usar un comparador personalizado.

Vamos con un ejemplo (es muy estúpido lo sé, pero los comparadores personalizados se usan en casos muy extremos, y es dificil encontrar ejemplos simples) que nos va ayudar a entender como funciona este operador.

IEnumerable<string> ciudades = ciudades1.Intersect(ciudades2, new ComparadorInicial());

¿Qué debería de devolver? Piensalo dos veces antes de probarlo.

Ahora bien, ¿Qué devuelve realmente?:

Cádiz
Málaga
Barcelona
Galicia

¿Y Bilbao? ¿Y Galicia? ¿Por qué no salen?

Vamos paso por paso:

Aquí lo importante es la inicial, nada más, entonces, ve que en la primera secuencia hay una C y que en la segunda también. Así que agrega la palabra de la primera secuencia que contiene dicha C, en este caso, Cádiz. ¿Qué pasa cuando llega a la B de Barcelona? Que comprueba si existe otra B en la segunda secuencia y si, hay otra, la de Bilbao. ¿Pero qué pasa? Que como antes, agrega el elemento de la primera secuencia, en este caso Barcelona ignorando Bilbao. Lo mismo pasa con Galicia y Granada.

Volver a la tabla de contenidos

Union

El operador Union proporciona la unión de conjuntos de dos secuencias. O lo que es lo mismo, mezcla los elementos de dos secuencias.

Código necesario para los ejemplos:

Una lista de ciudades:

List<string> ciudades1 = new List<string>{"Cádiz", "Málaga", "Murcia", "Madrid", "Barcelona",
										 "Galicia", "Bilbao"};

Otra lista de ciudades:

List<string> ciudades2 = new List<string>{"Cádiz", "Málaga", "Bilbao", "Granada", "Pamplona"};

Un comparador de iniciales:

public class ComparadorInicial : IEqualityComparer<string>
{
	public bool Equals(string x, string y)
	{
		return x[0] == y[0];
	}

	public int GetHashCode(string obj)
	{
		return obj[0].GetHashCode();
	}
}

Volver al operador Union

Union estándar:

public static IEnumerable<TSource> Union<TSource>(
	this IEnumerable<TSource> first,
	IEnumerable<TSource> second
)

El operador Union toma como parámetro la otra secuencia que usará para la unión. Devuelve una enumeración con la unión de las dos secuencias.

Tenemos dos secuencias con ciudades, queremos crear una enumeración que contenga las ciudades de ambas secuencias (sin repetir):

IEnumerable<string> ciudades = ciudades1.Union(ciudades2);

Nada que deba sorprendernos. El resultado:

Cádiz
Málaga
Murcia
Madrid
Barcelona
Galicia
Bilbao
Granada
Pamplona

Volver al operador Union

Union + comparador personalizado:

public static IEnumerable<TSource> Union<TSource>(
	this IEnumerable<TSource> first,
	IEnumerable<TSource> second,
	IEqualityComparer<TSource> comparer
)

Como todos los operadores de esta familia, tenemos la sobrecargar con el comparador personalizado.

Vamos a hacer una unión por iniciales:

IEnumerable<string> ciudades = ciudades1.Union(ciudades2, new ComparadorInicial());

Básicamente creará una enumeración donde se va devolviendo cada palabra siempre y cuando no haya otra que tenga la misma inicial. Como en los casos anteriores en esta familia. Primero se devuelve las palabras de la primera secuencia y luego de la segunda. Así que Bilbao por ejemplo no aparece puesto que se devolvió primero Barcelona.

El resultado es:

Cádiz
Málaga
Barcelona
Galicia
Pamplona

Volver a la tabla de contenidos

Tags:

Un comentario

Dejar un comentario