operadores de linq

111
Operadores de LINQ Jesús Rodríguez Rodríguez

Upload: neubaten

Post on 04-Aug-2015

245 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: Operadores de Linq

Operadores de LINQ

Jesús Rodríguez Rodríguez  

Page 2: Operadores de Linq

2 Operadores de LINQ

Primera Edición, Mayo de 2010 http://www.foxandxss.net Libro publicado bajo la licencia Creative Commons

 

Page 3: Operadores de Linq

3 Operadores de LINQ

OPERADORES DE LINQ 1 

1. FILTERING (FILTRADO) 6 

1.1 OFTYPE 6 

1.2 WHERE 7 

2. PROJECTION (PROYECCIÓN) 9 

2.1 SELECT 9 

3. PARTITIONING (PARTICIONADO) 17 

3.1 SKIP 17 

3.2 SKIPWHILE 18 

3.4 TAKEWHILE 21 

4. JOIN (UNIÓN) 23 

4.1 GROUPJOIN 23 

5. CONCATENATION (CONCATENACIÓN) 32 

5.1 CONCAT 32 

6. ORDERING (ORDENACIÓN) 33 

6.1 ORDERBY 33 

6.2 ORDERBYDESCENDING 35 

6.3 REVERSE 38 

6.4 THENBY 39 

6.5 THENBYDESCENDING 42 

7. GROUPING (AGRUPACIÓN) 45 

Page 4: Operadores de Linq

4 Operadores de LINQ

7.1 GROUPBY 45 

7.2 TOLOOKUP 55 

8. SET (CONJUNTO) 60 

8.1 DISTINCT 60 

8. SET (CONJUNTO) 62 

8.3 INTERSECT 64 

8.4 UNION 67 

9. CONVERSION (CONVERSIÓN) 70 

9.1 ASENUMERABLE 70 

9.2 ASQUERYABLE 71 

9.3 CAST 72 

9.4 TOARRAY 73 

9.5 TODICTIONARY 74 

9.6 TOLIST 78 

10. EQUALITY (IGUALDAD) 80 

10.1 SEQUENCEEQUAL 80 

11. ELEMENT (ELEMENTO) 82 

11.1 ELEMENTAT 82 

11.2 ELEMENTATORDEFAULT 82 

11.3 FIRST 83 

11.4 FIRSTORDEFAULT 84 

11.5 LAST 85 

11.6 LASTORDEFAULT 87 

11.7 SINGLE 88 

11.8 SINGLEORDEFAULT 89 

Page 5: Operadores de Linq

5 Operadores de LINQ

12. GENERATION (GENERACIÓN) 91 

12.1 DEFAULTIFEMPTY 91 

12.2 EMPTY 92 

12.3 RANGE 93 

12.4 REPEAT 94 

13. QUANTIFIER (CUANTIFICADORES) 95 

13.1 ALL 95 

13.2 ANY 96 

13.3 CONTAINS 97 

14. AGGREGATION (AGREGACIÓN) 100 

14.1 AGGREGATE 100 

14.2 AVERAGE 102 

14.3 COUNT 104 

14.4 LONGCOUNT 105 

14.5 MAX 106 

14.6 MIN 108 

14.7 SUM 109 

Page 6: Operadores de Linq

6 1. Filtering (Filtrado)

1. Filtering (Filtrado)

1.1 OfType

El operador OfType nos permite filtrar una secuencia dado un tipo.

1.1.1 Código necesario para los ejemplos

Lista de objetos al azar:

object[] objetos = { "LINQ", 1, 23.4F, "Perro", new object(), "Casa" };

1.1.2 OfType

public static IEnumerable<TResult> OfType<TResult>( this IEnumerable source )

Le especificamos el tipo que queremos filtrar y nos devuelve una enumeración de dicho tipo.

Por ejemplo, si queremos solo los elementos del tipo string usamos el operador OfType para filtrar esa secuencia:

IEnumerable<string> cadenas = objetos.OfType<string>();

Si imprimimos el resultado, la salida sería:

LINQ

Perro

Casa

Page 7: Operadores de Linq

7 Operadores de LINQ

1.2 Where

El operador Where nos permite filtrar una secuencia, pero a diferencia de OfType, el operador Where es mucho más flexible puesto que admite como parámetro un delegado.

1.2.1 Código necesario para los ejemplos

Lista de títulos (imaginarios) de libros:

var libros = new List<string> { "Programando en C#", "LINQ para torpes", "WPF para todos", "Empezando con LINQ", "LINQ Avanzado" };

1.2.2 Where estándar

public static IEnumerable<TSource> Where<TSource>( this IEnumerable<TSource> source, Func<TSource, bool> predicate )

En este caso Where acepta un delegado el cual recibe como parámetro un elemento de la secuencia y nos devuelve un bool que nos indica si ese elemento se filtra o no. El operador devuelve una enumeración con los elementos filtrados.

Queremos una nueva lista de libros donde sólo estén los que sean de LINQ, para ello usamos Where.

IEnumerable<string> librosLINQ = libros.Where( libro => libro.Contains("LINQ"));

Además, el operador Where se puede convertir a la sintaxis de un query expression:

IEnumerable<string> librosLINQ = from libro in libros where libro.Contains("LINQ") select libro;

Page 8: Operadores de Linq

8 1. Filtering (Filtrado)

En ambos casos, el resultado de imprimirlo sería:

LINQ para torpes

Empezando con LINQ

LINQ Avanzado

1.2.3 Where + índice

public static IEnumerable<TSource> Where<TSource>( this IEnumerable<TSource> source, Func<TSource, int, bool> predicate )

En este caso, el delegado acepta también un int el cual representa la posición de cada elemento en la secuencia que estamos filtrando.

Así que si por ejemplo queremos aquellos libros de LINQ que estén dentro de las 3 primeras posiciones de la secuencia, podemos usar el siguiente fragmento:

IEnumerable<string> librosLINQ = libros.Where((libro, index) => libro.Contains("LINQ") && index < 3);

Esto imprimiría:

LINQ para Torpes

La versión de Where que acepta también el índice, no puede ser convertida a la sintaxis de los query expressions.

Page 9: Operadores de Linq

9 Operadores de LINQ

2. Projection (Proyección)

2.1 Select

El operador Select nos permite proyectar cada elemento de una secuencia a una nueva enumeración.

2.1.1 Código necesario para los ejemplos

Una pequeña clase representando una persona con una lista de gadgets:

public class Persona { public string Nombre { get; set; } public int Edad { get; set; } public List<string> Gadgets { get; set; } public Persona(string nombre, int edad, List<string> gadgets) { Nombre = nombre; Edad = edad; Gadgets = gadgets; } }

Y una lista de dichas personas:

List<Persona> personas = new List<Persona> { new Persona("Jesus", 24, new List<string> {"Ipod", "HTC Diamond"} ), new Persona("Juan", 20, new List<string> {"Vaio", "Android"} ), new Persona("Alvaro", 24, new List<string> {"Pentium",

"Movil Ladrillo"} ) };

Page 10: Operadores de Linq

10 2. Projection (Proyección)

2.1.2 Select estándar

public static IEnumerable<TResult> SelectMany<TSource, TResult>( this IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector )

El argumento es un delegado que acepta un elemento de la secuencia y devuelve el elemento que queramos proyectar. El operador devuelve una enumeración de aquellos elementos que hemos proyectado.

Queremos una lista solo con los nombres de las personas, para ello hemos de proyectar los nombres de cada persona en una nueva enumeración:

IEnumerable<string> nombres = personas.Select( persona => persona.Nombre);

Podemos convertir el operador Select a la sintaxis de un query expression:

IEnumerable<string> nombres = from persona in personas select persona.Nombre;

En ambos casos el resultado de imprimir la secuencia sería:

Juan

Alvaro

Además podemos crear tipos anónimos proyectando solo las partes que queramos de una clase, por ejemplo, si solo queremos el nombre y la edad:

var nombreYEdad = from persona in personas select new { persona.Nombre, persona.Edad };

Esto crearía un tipo anónimo y dentro sería algo así:

Nombre=Jesus Edad=24

Nombre=Juan Edad=20

Nombre=Alvaro Edad=25

Page 11: Operadores de Linq

11 Operadores de LINQ

Por otro lado, si quisiéramos imprimir la lista de gadgets, haríamos algo así:

<List<string>> ListasGadgets = from persona in personas select persona.Gadgets;

Esa proyección devuelve una enumeración de listas, así que tendríamos que hacer dos iteraciones para mostrar los gadgets:

foreach (var lista in ListasGadgets) foreach (var gadget in lista) Console.WriteLine(gadget);

La salida como cabe de esperar es:

Ipod

HTC Diamond

Vaio

Android

Pentium

Movil Ladrillo

Podemos ver una versión mejorada de este último ejemplo usando SelectMany.

2.1.3 Select + Índice

public static IEnumerable<TResult> Select<TSource, TResult>( this IEnumerable<TSource> source, Func<TSource, int, TResult> selector )

El delegado recibe el elemento en cuestión, además del índice de la posición del elemento en la secuencia.

Vamos a suponer que queremos crear un tipo anónimo con cada índice y nombre:

var nombres = personas.Select((persona, indice) => new { persona.Nombre, indice });

Page 12: Operadores de Linq

12 2. Projection (Proyección)

El resultado es:

Nombre=Jesus indice=0

Nombre=Juan indice=1

Nombre=Alvaro indice=2

Esta sobrecarga del operador Select no admite su forma en query expression.

2.2 SelectMany

El operador SelectMany es similar al operador Select a excepción de que SelectMany coge cada elemento que proyecta, lo convierte en una enumeración, y luego concatena todas las enumeraciones.

2.2.1 Código necesario para los ejemplos

Una clase representando una persona y una lista de gadgets:

public class Persona { public string Nombre { get; set; } public int Edad { get; set; } public List<string> Gadgets { get; set; } public Persona(string nombre, int edad, List<string> gadgets) { Nombre = nombre; Edad = edad; Gadgets = gadgets; } }

Y una lista de dichas personas:

List<Persona> personas = new List<Persona> { new Persona("Jesus", 24, new List<string> {"Ipod", "HTC Diamond"} ), new Persona("Juan", 20, new List<string> {"Vaio", "Android"} ),

Page 13: Operadores de Linq

13 Operadores de LINQ

new Persona("Alvaro", 24, new List<string> {"Pentium",

"Movil Ladrillo"} ) };

2.2.2 SelectMany estándar

public static IEnumerable<TResult> SelectMany<TSource, TResult>( this IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector )

El argumento que toma es un delegado el cual recibe el elemento y devuelve una enumeración con la proyección de cada elemento como una enumeración. Finalmente devuelve todo en una enumeración.

Como este operador es difícil de entender, vamos a compararlo directamente con Select.

Queremos una lista con todos los nombres de los gadgets, con Select ya hemos dicho que se haría así:

List<string>> ListasGadgets = from persona in personas select persona.Gadgets;

El problema es que esto es una enumeración de listas, y para poder imprimir los resultados necesitamos 2 bucles:

foreach (var lista in ListasGadgets) foreach (var gadget in lista) Console.WriteLine(gadget);

Con SelectMany esto es mucho más simple:

IEnumerable<string> gadgets = personas.SelectMany(persona => persona.Gadgets);

Lo que hace exactamente es coger cada lista de gadgets y las concatena una con otra y el resultado es una enumeración con los elementos de cada lista de gadgets. Justo lo que estábamos buscando.

Puedes verlo de otra forma usando Select en un query expression:

IEnumerable<string> gadgets = from persona in personas from gadget in persona.Gadgets select gadget;

Page 14: Operadores de Linq

14 2. Projection (Proyección)

Por cada persona de la lista y por cada gadget de cada persona, proyectamos el gadget.

Por supuesto es más cómodo usar SelectMany.

2.2.3 SelectMany + Índice

public static IEnumerable<TResult> SelectMany<TSource, TResult>( this IEnumerable<TSource> source, Func<TSource, int, IEnumerable<TResult>> selector )

El delegado, además del elemento, toma un int el cual es el índice del elemento. Devuelve una enumeración con la proyección de cada elemento como una enumeración.

Queremos una lista de gadgets que además contenga el índice del elemento que pertenece (instancia de Persona):

El resultado es:

gadget=Ipod index=0

gadget=HTC Diamond index=0

gadget=Vaio index=1

gadget=Android index=1

gadget=Pentium index=2

gadget=Movil Ladrillo index=2

Como podéis ver, "Ipod" y "HTC Diamond" pertenecen al primer elemento, así que el índice es 0.

2.2.4 SelectMany + Función

public static IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>( this IEnumerable<TSource> source, Func<TSource, IEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector )

Page 15: Operadores de Linq

15 Operadores de LINQ

En ésta sobrecarga, recibimos también un delegado que se va a ejecutar por cada elemento que vayamos proyectando. Dicho delegado recibe el elemento de la secuencia y el elemento que estamos proyectando.

Por ejemplo, si queremos convertir los nombres de los gadgets a mayúsculas, usaríamos algo así:

IEnumerable<string> gadgets = personas.SelectMany(persona => persona.Gadgets,

(persona, gadget) => gadget.ToUpper());

Como se puede ver, el segundo parámetro que le estamos pasando es una lambda que recibe dos parámetros: el objeto persona que es el elemento de la secuencia y cada elemento que estamos proyectando; luego simplemente le aplicamos el método ToUpper a cada gadget y ya está.

El resultado es:

IPOD

HTC DIAMOND

VAIO

ANDROID

PENTIUM

MOVIL LADRILLO

2.2.5 SelectMany + Función + Índice

public static IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>( this IEnumerable<TSource> source, Func<TSource, int, IEnumerable<TCollection>> CollectionSelector, Func<TSource, TCollection, TResult> resultSelector )

Esta sobrecarga es una combinación entre las dos anteriores, tiene la función que se va a aplicar a cada elemento proyectado y contiene el índice de cada elemento de la secuencia.

Vamos a combinar ambos ejemplos anteriores para crear uno nuevo.

Page 16: Operadores de Linq

16 2. Projection (Proyección)

Queremos imprimir los gadgets en plan:

NUMERO – GADGET

Para ello usaremos esto:

IEnumerable<string> gadgets = personas.SelectMany((persona, index) => persona.Gadgets.Select(gadget => index + " - " + gadget),

(persona, gadget) => gadget.ToUpper());

Básicamente recorremos cada elemento de la secuencia y de cada elemento proyectamos una cadena que contiene el índice y el nombre del gadget, posteriormente llamamos a ToUpper para ponerlo en mayúsculas.

Algo más complicado que el resto, pero no es precisamente la sobrecarga de SelectMany más sencilla.

El resultado es el que esperamos:

0 - IPOD

0 - HTC DIAMOND

1 - VAIO

1 - ANDROID

2 - PENTIUM

2 - MOVIL LADRILLO

Page 17: Operadores de Linq

17 Operadores de LINQ

3. Partitioning (Particionado)

3.1 Skip

El operador Skip nos sirve para ignorar los X primeros elementos de una secuencia.

3.1.1 Código necesario para los ejemplos

Una lista de nombres:

List<string> nombres = new List<string> { "Jesus", "Alvaro", "Manolo", "Juan", "Paco", "Rosi", "Maria", "Amanda", "Julia" };

3.1.2 Skip

public static IEnumerable<TSource> Skip<TSource>( this IEnumerable<TSource> source, int count )

Skip toma como argumento un número que será la cantidad de elementos de la secuencia a ignorar. Devuelve una enumeración con los elementos restantes.

Teniendo la lista de nombre, queremos saltarnos los 5 primeros para así quedarnos con los nombres de mujer:

IEnumerable<string> mujeres = nombres.Skip(5);

Page 18: Operadores de Linq

18 3. Partitioning (Particionado)

El resultado sería:

Rosi

Maria

Amanda

Julia

Si como argumento le pasamos un cero o un número negativo, no ignorará nada.

3.2 SkipWhile

El operador SkipWhile como su homónimo Skip sirve para ignorar elementos. Pero dada una condición y no un número.

3.2.1 Código necesario para los ejemplos

Una lista de calificaciones:

List<double> notas = new List<double> { 0, 1.2, 2, 3, 4.5, 4.9, 6, 6, 7.6, 9, 10 };

3.2.2 SkipWhile estándar

public static IEnumerable<TSource> SkipWhile<TSource>( this IEnumerable<TSource> source, Func<TSource, bool> predicate )

SkipWhile toma un delegado el cual recibe el elemento de la secuencia y devuelve un bool. El operador devuelve una enumeración con los elementos no ignorados.

Cada elemento irá pasando por el delegado y devolverá true o false dependiendo de una condición. Mientras vaya devolviendo true, esos elementos se irán ignorando. Lo importante de esto es que cuando uno de los elementos devuelva false, se devuelve este elemento y los restantes de la secuencia sin comprobarlos.

Este operador se suele usar con los operadores de ordenación, pero puesto que prefiero mostrar ejemplos que involucren la menor cantidad

Page 19: Operadores de Linq

19 Operadores de LINQ

de operadores posibles, vamos a probar con una lista ya ordenada de antemano.

Imaginemos que nuestra condición es ir ignorando toda calificación considerada suspendida hasta que encontremos una aprobada:

IEnumerable<double> aprobados = notas.SkipWhile(nota => nota < 5);

Aquí la condición es sencilla, ignorar toda nota que sea menor a 5. Como veis es importante que la secuencia a tratar esté ordenada puesto que cuando encuentre una nota mayor a 5, dejará de hacer comprobaciones y meterá en la enumeración todas las notas restantes estén aprobadas o no.

El resultado es:

6

6

7.6

9

10

3.2.3 SkipWhile + índice

public static IEnumerable<TSource> SkipWhile<TSource>( this IEnumerable<TSource> source, Func<TSource, int, bool> predicate )

En esta sobrecarga, el delegado además del elemento, recibe también el índice de dicho elemento en la secuencia. Lo cual nos da más posibilidades a la hora de ignorar elementos.

Como antes, es difícil mostrar el uso de esta sobrecarga si no es en conjunción con otros operadores, pero como la sencillez es lo más importante a la hora de aprender, vamos con este ejemplo:

IEnumerable<double> variasNotas = notas.SkipWhile((nota, index) => nota < index + 0.5);

Estamos ignorando todas las notas hasta que una de ellas sea más alta que su índice + 0.5.

Page 20: Operadores de Linq

20 3. Partitioning (Particionado)

Así que el resultado es:

4,5

4,9

6

6

7,6

9

10

3.3 Take

El operador Take es justo lo contrario al operador Skip Tomará aquellos X primeros elementos de una secuencia e ignorará el resto.

3.3.1 Código necesario para los ejemplos

Una lista de nombres:

List<string> nombres = new List<string> { "Jesus", "Alvaro", "Manolo", "Juan", "Paco", "Rosi", "Maria", "Amanda", "Julia" };

3.3.2 Take

public static IEnumerable<TSource> Take<TSource>( this IEnumerable<TSource> source, int count )

Take toma como argumento un número que será la cantidad de elementos de la secuencia a devolver. Devuelve una enumeración con dichos elementos ignorando el resto.

En este caso, si necesitamos los 5 primeros nombres, haríamos algo así:

IEnumerable<string> hombres = nombres.Take(5);

Page 21: Operadores de Linq

21 Operadores de LINQ

Esto imprimiría:

Jesus

Alvaro

Manolo

Juan

Paco

Si le pasamos un número negativo o cero, no devolverá nada.

3.4 TakeWhile

El operador TakeWhile como su homónimo Take sirve para devolver los elementos de una secuencia pero en este caso los irá devolviendo mientras la condición devuelva true.

3.4.1 Código necesario para los ejemplos

Una lista de calificaciones:

List<double> notas = new List<double> { 0, 1.2, 2, 3, 4.5, 4.9, 6, 6, 7.6, 9, 10 };

3.4.2 TakeWhile estándar

public static IEnumerable<TSource> TakeWhile<TSource>( this IEnumerable<TSource> source, Func<TSource, bool> predicate )

TakeWhile toma un delegado el cual recibe el elemento de la secuencia y devuelve un bool. El operador devuelve una enumeración con los elementos no ignorados.

Cada elemento irá pasando por el delegado y devolverá true o false dependiendo de una condición. Mientras devuelva true, los elementos se irán devolviendo. Lo importante de esto es que cuando uno de los elementos devuelva false, se ignorará este elemento y los restantes de la enumeración sin comprobarlos.

Este operador se suele usar con los operadores de ordenación, pero puesto que prefiero mostrar ejemplos que involucren la menor cantidad

Page 22: Operadores de Linq

22 3. Partitioning (Particionado)

de operadores posibles, vamos a probar con una lista ya ordenada a mano.

Vamos a coger todas las notas suspensas (para regañar a aquellos que no aprobaron :P):

IEnumerable<double> suspensos = notas.TakeWhile(nota => nota < 5);

Mientras la nota sea menor a 5, ir devolviéndola. Cuando una sea mayor, ignorar el resto de la secuencia.

El resultado es:

0

1.2

2

3

4.5

4.9

3.4.3 TakeWhile + Índice

public static IEnumerable<TSource> TakeWhile<TSource>( this IEnumerable<TSource> source, Func<TSource, int, bool> predicate )

En esta sobrecarga, el delegado además del elemento, recibe también el índice de dicho elemento en la secuencia. Lo cual nos da más posibilidades a la hora de devolver elementos.

IEnumerable<double> variasNotas = notas.TakeWhile((nota, index) => nota < index + 0.5);

Mientras la nota sea menor al valor del índice + 0.5, añadirla a la enumeración.

Page 23: Operadores de Linq

23 Operadores de LINQ

4. Join (Unión)

4.1 GroupJoin

El operador GroupJoin nos permite establecer una relación entre 2 secuencias y agrupar los resultados. Básicamente agrupa un elemento de la secuencia externa a una colección de elementos de la secuencia interna.

4.1.1 GroupJoin estándar

4.1.1.1 Código necesario

Una clase Provincia:

public class Provincia { public string Nombre { get; set; } public Provincia(string nombre) { Nombre = nombre; } }

Una clase Ciudad:

public class Ciudad { public string Nombre { get; set; } public Provincia Localizacion { get; set; } public Ciudad(string nombre, Provincia localizacion) { Nombre = nombre; Localizacion = localizacion; } }

Creamos varias provincias y varias ciudades:

Provincia cadiz = new Provincia("Cádiz"); Provincia malaga = new Provincia("Málaga"); Provincia madrid = new Provincia("Madrid"); Provincia barcelona = new Provincia("Barcelona"); Ciudad algeciras = new Ciudad("Algeciras", cadiz); Ciudad jerez = new Ciudad("Jerez", cadiz);

Page 24: Operadores de Linq

24 4. Join (Unión)

Ciudad ronda = new Ciudad("Ronda", malaga); Ciudad churriana = new Ciudad("Churriana", malaga); Ciudad alcobendas = new Ciudad("Alcobendas", madrid); Ciudad fuenlabrada = new Ciudad("Fuenlabrada", madrid);

Y las metemos en listas:

List<Provincia> listaProvincias = new List<Provincia> { cadiz, malaga, madrid, barcelona }; List<Ciudad> listaCiudades = new List<Ciudad> { algeciras, jerez, ronda, churriana, alcobendas, fuenlabrada };

4.1.1.2 Definición

public static IEnumerable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>( this IEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, IEnumerable<TInner>, TResult> resultSelector )

El operador recibe varios parámetros:

Recibe una secuencia la cual llamaremos secuencia interna (La externa es en la que aplicamos el operador).

Recibe un delegado que usaremos para extraer la clave de la secuencia externa.

Recibe un delegado que usaremos para extraer la clave de la secuencia interna.

El último delegado lo usaremos para crear la enumeración que saldrá como resultado al combinar un elemento de la secuencia externa con una colección de elementos de la secuencia interna.

El operador devuelve la enumeración resultante.

Vamos a verlo con un ejemplo. Queremos crear una enumeración donde agruparemos las provincias con una colección de ciudades que están dentro de dicha provincia:

var query = listaProvincias.GroupJoin(listaCiudades, provincia => provincia, ciudades => ciudades.Localizacion, (provincia, ciudades) => new { provincia, ciudades });

Page 25: Operadores de Linq

25 Operadores de LINQ

Por pasos:

El primer parámetro es la secuencia interna, en este caso la lista de ciudades (La externa es la lista de provincias).

La clave externa será el elemento en si (de la secuencia externa).

La clave interna será la propiedad Localizacion de cada ciudad.

Luego creamos un tipo anónimo que consistirá en una provincia más una lista de ciudades.

Parece complicado pero no lo es, simplemente colocamos como clave los 2 elementos que harán la unión, en este caso un objeto Provincia con otro objeto Provincia dentro de cada ciudad. Así que simplemente cada provincia estará asociada a una lista de ciudades que la tengan como provincia.

Como alternativa, podemos usar la sintaxis de los query expression:

var query = from ciudad in listaCiudades group ciudad by ciudad.Localizacion into ciudades select new { ciudades.Key.Nombre, ciudades };

Por cada ciudad de la lista, meterlas en un grupo que tengan la Localización en común. Llamar a ese grupo ciudades. De ahí creamos un tipo anónimo con el nombre de la clave (es el elemento que hemos usado para hacer la unión, en este caso la provincia) y la lista de ciudades de dicho grupo.

Es lo mismo pero con la sintaxis alternativa que nos da algunos operadores de LINQ.

En ambos casos el resultado podría ser así:

Cádiz - (Algeciras, Jerez)

Málaga - (Ronda, Churriana)

Madrid - (Alcobendas, Fuenlabrada)

Barcelona - ()

Page 26: Operadores de Linq

26 4. Join (Unión)

4.1.2 GroupJoin + Comparador personalizado

4.1.2.1 Código necesario

Una lista de iniciales y otra de palabras:

List<string> listaIniciales = new List<string> { "A", "B", "C", "D" };

List<string> listaPalabras = new List<string> { "Arroz", "Antena",

"Barco", "Caballo", "Decir" };

Un comparador personalizado:

public class ComparadorIniciales : IEqualityComparer<string> { public bool Equals(string x, string y) { return x[0] == y[0]; } public int GetHashCode(string obj) { return obj[0].GetHashCode(); } }

4.1.2.2 Definición

public static IEnumerable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>( this IEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, IEnumerable<TInner>, TResult> resultSelector, IEqualityComparer<TKey> comparer )

Esta sobrecarga hace lo mismo que el operador estándar, simplemente nos da la posibilidad de usar un comparador personalizado.

En el anterior ejemplo, habíamos simplemente comparado 2 instancias de por si iguales, ahora vamos a poner un ejemplo donde necesitaremos un comparador personalizado.

Tenemos una lista con iniciales y una lista de palabras. Queremos hacer una unión donde cada inicial estará asociada a una colección de palabras que empiecen por dicha inicial.

Page 27: Operadores de Linq

27 Operadores de LINQ

El código sería:

var query = listaIniciales.GroupJoin(listaPalabras, inicial => inicial, palabras => palabras, (inicial, palabras) => new { inicial, palabras }, new ComparadorIniciales());

La forma de usarlo es la misma que con el operador estándar, simplemente añadimos una instancia de nuestro comparador personalizado que hará el trabajo de comprobar si una inicial y una palabra está relacionada.

Por ser la primera vez que usamos un comparador personalizado, lo explicaré un poco.

El comparador ha de implementar IEqualityComparer<T> y dicha interfaz viene con 2 métodos.

Por cada elemento comprobará el hashcode y cuando 2 elementos tienen el mismo hashcode llamará a equals para comprobar si esos dos elementos son iguales.

Por otro lado, cabe decir que jamás de los jamases usaríamos esta sobrecarga para hacer un trabajo como este, puesto que el operador estándar es capaz de hacer una unión de este tipo o directamente usar otro operador como SelectMany. Simplemente esto es un ejemplo para que veáis como implementar un comparador personalizado y usarlo.

Finalmente el resultado es algo tipo:

A - (Arroz, Antena)

B - (Barco)

C - (Caballo)

D - (Decir)

Page 28: Operadores de Linq

28 4. Join (Unión)

4.2 Join

El operador Join sirve para establecer una relación entre 2 secuencias comparando sus claves.

4.2.1 Join estándar

4.2.1.1 Código necesario

Una clase Provincia:

public class Provincia { public string Nombre { get; set; } public Provincia(string nombre) { Nombre = nombre; } }

Una clase Ciudad:

public class Ciudad { public string Nombre { get; set; } public Provincia Localizacion { get; set; } public Ciudad(string nombre, Provincia localizacion) { Nombre = nombre; Localizacion = localizacion; } }

Creamos varias provincias y varias ciudades:

Provincia cadiz = new Provincia("Cádiz"); Provincia malaga = new Provincia("Málaga"); Provincia madrid = new Provincia("Madrid"); Provincia barcelona = new Provincia("Barcelona"); Ciudad algeciras = new Ciudad("Algeciras", cadiz); Ciudad jerez = new Ciudad("Jerez", cadiz); Ciudad ronda = new Ciudad("Ronda", malaga); Ciudad churriana = new Ciudad("Churriana", malaga); Ciudad alcobendas = new Ciudad("Alcobendas", madrid); Ciudad fuenlabrada = new Ciudad("Fuenlabrada", madrid);

Page 29: Operadores de Linq

29 Operadores de LINQ

Y las metemos en listas:

List<Provincia> listaProvincias = new List<Provincia> { cadiz, malaga, madrid, barcelona }; List<Ciudad> listaCiudades = new List<Ciudad> { algeciras, jerez, ronda, churriana, alcobendas, fuenlabrada };

4.2.1.2 Definición

public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>( this IEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, TInner, TResult> resultSelector )

El operador recibe varios parámetros:

Recibe una secuencia la cual llamaremos secuencia interna (La externa es en la que aplicamos el operador).

Recibe un delegado que usaremos para extraer la clave de la secuencia externa.

Recibe un delegado que usaremos para extraer la clave de la secuencia interna.

El último delegado lo usaremos para crear la enumeración que saldrá como resultado al combinar un elemento de la secuencia externa con un elemento de la secuencia interna.

El operador devuelve la enumeración resultante.

Por ejemplo, queremos una secuencia compuesta por una provincia y una ciudad que pertenezca a esa provincia:

var query = listaProvincias.Join(listaCiudades, provincia => provincia, ciudad => ciudad.Localizacion, (provincia, ciudad) => new { provincia, ciudad });

A diferencia del operador GroupJoin aquí se creará un elemento por cada relación. Eso quiere decir, que si tenemos 2 ciudades de Cádiz, saldrán 2 entradas en vez de 1 con varios elementos.

Page 30: Operadores de Linq

30 4. Join (Unión)

Esto es lo que comúnmente se llama un Inner Join que básicamente es encontrar la relación entre 2 secuencias, y cada elemento de las 2 secuencias que se relacionen irán a parar a una nueva enumeración.

Podemos representar un Inner Join usando la sintaxis de un query expression:

var query = from provincia in listaProvincias join ciudad in listaCiudades on provincia equals ciudad.Localizacion select new { provincia, ciudad };

Tiene un extraño parecido a un Inner Join en SQL ¿Verdad? :)

En ambos casos el resultado podría ser del tipo:

Cádiz - Algeciras

Cádiz - Jerez

Málaga - Ronda

Málaga - Churriana

Madrid - Alcobendas

Madrid – Fuenlabrada

4.2.2 Join + Comparador personalizado

4.2.2.1 Código necesario

Una lista de iniciales y otra de palabras:

List<string> listaIniciales = new List<string> { "A", "B", "C", "D" };

List<string> listaPalabras = new List<string> { "Arroz", "Antena",

"Barco", "Caballo", "Decir" };

Un comparador personalizado:

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

Page 31: Operadores de Linq

31 Operadores de LINQ

return obj[0].GetHashCode(); } }

4.2.2.2 Definición

public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>( this IEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, TInner, TResult> resultSelector, IEqualityComparer<TKey> comparer )

Esta sobrecarga hace lo mismo que el operador estándar, simplemente nos da la posibilidad de usar un comparador personalizado.

En el anterior ejemplo, habíamos simplemente comparado 2 instancias de por si iguales, ahora vamos a poner un ejemplo donde necesitaremos un comparador personalizado.

Tenemos una lista con iniciales y una lista de palabras. Queremos hacer una unión donde cada inicial estará asociada a unas palabras que empiecen por dicha inicial:

var query = listaIniciales.Join(listaPalabras, inicial => inicial, palabra => palabra, (inicial, palabra) => new { inicial, palabra }, new ComparadorIniciales());

Si necesitas saber cómo crear y usar un comparador revisa la definición del operador GroupJoin.

El resultado es:

A - Arroz

A - Antena

B - Barco

C - Caballo

D – Decir

Page 32: Operadores de Linq

32 5. Concatenation (Concatenación)

5. Concatenation (Concatenación)

5.1 Concat

El operador Concat sirve para concatenar 2 secuencias.

5.1.1 Código necesario para los ejemplos

Un par de listas de nombres:

List<string> nombres1 = new List<string> { "Jesus", "Alvaro", "Manolo" }; List<string> nombres2 = new List<string> { "Rosi", "Amanda", "Julia" };

5.1.2 Concat

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

Concat recibe como parámetro una secuencia que queremos concatenar a la secuencia que está ejecutando el operador. Devuelve una sola enumeración que contiene las dos enumeraciones usadas.

Por ejemplo, si queremos concatenar las dos listas de nombres anteriores en una sola enumeración:

IEnumerable<string> nombres = nombres1.Concat(nombres2);

El resultado sería:

Jesus

Alvaro

Manolo

Rosi

Amanda

Julia

Page 33: Operadores de Linq

33 Operadores de LINQ

6. Ordering (Ordenación)

6.1 OrderBy

El operador OrderBy ordena una secuencia de forma ascendente dada una clave.

6.1.1 Código necesario para los ejemplos

Una clase persona:

public class Persona { public string Nombre { get; set; } public int Edad { get; set; } public Persona(string nombre, int edad) { Nombre = nombre; Edad = edad; } }

Una lista de personas:

List<Persona> personas = new List<Persona> { new Persona("Rodriguez, Jesús", 24), new Persona("Perez, Juan", 15), new Persona("García, Javier", 45), new Persona("Toledo, María", 37) };

Un comparador personalizado:

public class OrdenaNombre : IComparer<string> { public int Compare(string x, string y) { string nombreX = x.Split()[1]; string nombreY = y.Split()[1]; return nombreX.CompareTo(nombreY); } }

Page 34: Operadores de Linq

34 6. Ordering (Ordenación)

6.1.2 OrderBy estándar

public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector )

OrderBy toma como parámetro la clave que se usará para ordenar la secuencia. Devuelve una enumeración ordenada.

Por ejemplo, si queremos ordenar la lista de personas por el nombre, usaríamos este código:

IEnumerable<Persona> ascendente = personas.OrderBy( persona => persona.Nombre);

La clave en este caso es la propiedad Nombre del elemento de la secuencia.

Como alternativa, podemos usar la sintaxis de los query expressions:

IEnumerable<Persona> ascendente = from persona in personas orderby persona.Nombre select persona;

El resultado sería:

Nombre=García, Javier Edad=45

Nombre=Perez, Juan Edad=15

Nombre=Rodriguez, Jesús Edad=24

Nombre=Toledo, María Edad=37

6.1.3 OrderBy + Comparador personalizado:

public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer )

Aquí además de la clave que usaremos para ordenar, usaremos un comparador personalizado.

Page 35: Operadores de Linq

35 Operadores de LINQ

Por ejemplo, queremos ordenar otra vez usando la propiedad Nombre pero en este caso en vez de ordenar por el apellido vamos a ordenar por el nombre en sí.

Para ello cogemos el comparador OrdenaNombre que he adjuntado en el código que necesitamos para trabajar con este operador, con ello escribimos:

IEnumerable<Persona> ascendente = personas.OrderBy(persona => persona.Nombre, new OrdenaNombre());

El comparador extraerá el nombre de cada propiedad y los comparará.

El resultado es:

Nombre=García, Javier Edad=45

Nombre=Rodriguez, Jesús Edad=24

Nombre=Perez, Juan Edad=15

Nombre=Toledo, María Edad=37

6.2 OrderByDescending

El operador OrderByDescending ordena una secuencia de forma descendente dada una clave.

6.2.1 Código necesario para los ejemplos

Una clase persona:

public class Persona { public string Nombre { get; set; } public int Edad { get; set; } public Persona(string nombre, int edad) { Nombre = nombre; Edad = edad; } }

Page 36: Operadores de Linq

36 6. Ordering (Ordenación)

Una lista de personas:

List<Persona> personas = new List<Persona> { new Persona("Rodriguez, Jesús", 24), new Persona("Perez, Juan", 15), new Persona("García, Javier", 45), new Persona("Toledo, María", 37) };

Un comparador personalizado:

public class OrdenaNombre : IComparer<string> { public int Compare(string x, string y) { string nombreX = x.Split()[1]; string nombreY = y.Split()[1]; return nombreX.CompareTo(nombreY); } }

6.2.2 OrderByDescending estándar

public static IOrderedEnumerable<TSource> OrderByDescending<TSource, TKey>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector )

OrderByDescending toma como parámetro la clave que se usará para ordenar la secuencia (de forma descendente). Devuelve una enumeración ordenada.

Por ejemplo, si queremos ordenar la lista de personas por el nombre (de forma descendente), usaríamos este código:

IEnumerable<Persona> descendente = personas.OrderByDescending(persona => persona.Nombre);

La clave en este caso es la propiedad Nombre del elemento de la secuencia.

Page 37: Operadores de Linq

37 Operadores de LINQ

Como alternativa, podemos usar la sintaxis de los query expressions:

IEnumerable<Persona> descendente = from persona in personas orderby persona.Nombre descending select persona;

El resultado sería:

Nombre=Toledo, María Edad=37

Nombre=Rodriguez, Jesús Edad=24

Nombre=Perez, Juan Edad=15

Nombre=García, Javier Edad=45

6.2.3 OrderByDescending + Comparador personalizado

public static IOrderedEnumerable<TSource> OrderByDescending<TSource, TKey>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer )

Aquí además de la clave que usaremos para ordenar, usaremos un comparador personalizado.

Por ejemplo, queremos ordenar otra vez usando la propiedad Nombre pero en este caso en vez de ordenar por el apellido vamos a ordenar por el nombre en sí.

Para ello cogemos el comparador OrdenaNombre que he adjuntado en el código que necesitamos para trabajar con este operador, con ello escribimos:

IEnumerable<Persona> descendente = personas.OrderByDescending(persona => persona.Nombre,

new OrdenaNombre());

El comparador extraerá el nombre de cada propiedad y los comparará.

El resultado es:

Nombre=Toledo, María Edad=37

Nombre=Perez, Juan Edad=15

Nombre=Rodriguez, Jesús Edad=24

Nombre=García, Javier Edad=45

Page 38: Operadores de Linq

38 6. Ordering (Ordenación)

6.3 Reverse

El operador Reverse invierte una secuencia.

6.3.1 Código necesario para los ejemplos

Una lista de cadenas representando números:

List<string> numeros = new List<string> { "Uno", "Dos", "Tres", "Cuatro", "Cinco" };

6.3.2 Reverse

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

Este operador no toma ningún parámetro cuando lo usas en una secuencia (ya que el parámetro que empieza por this indica que es un método de extensión).

Cuando lo usas como un método de extensión en una secuencia, modifica la misma secuencia en sí pero no devuelve nada.

Por ejemplo:

numeros.Reverse();

Eso le daría la vuelta a la lista de números directamente.

O sea:

Cinco

Cuatro

Tres

Dos

Uno

Page 39: Operadores de Linq

39 Operadores de LINQ

6.4 ThenBy

El operador ThenBy toma una enumeración ordenada y le hace una ordenación adicional usando otra clave.

6.4.1 Código necesario para los ejemplos

Una clase persona:

public class Persona { public string Nombre { get; set; } public int Edad { get; set; } public Persona(string nombre, int edad) { Nombre = nombre; Edad = edad; } }

Una lista de personas:

List<Persona> personas = new List<Persona> { new Persona("Rodriguez, Jesús", 24), new Persona("Rodriguez, Jesús", 15), new Persona("Perez, Juan", 15), new Persona("García, Javier", 24), new Persona("Toledo, María", 37) };

Un comparador personalizado:

public class OrdenaNombre : IComparer<string> { public int Compare(string x, string y) { string nombreX = x.Split()[1]; string nombreY = y.Split()[1]; return nombreX.CompareTo(nombreY); } }

Page 40: Operadores de Linq

40 6. Ordering (Ordenación)

6.4.2 ThenBy estándar

public static IOrderedEnumerable<TSource> ThenBy<TSource, TKey>( this IOrderedEnumerable<TSource> source, Func<TSource, TKey> keySelector )

El operador ThenBy toma como argumento la clave que se usará para la ordenación adicional. Devuelve la enumeración ordenada.

Por ejemplo, queremos ordenar la lista de personas primero por su nombre y como ordenación adicional, la edad:

IEnumerable<Persona> dobleOrdenación = personas.OrderBy( persona => persona.Nombre).ThenBy(persona => persona.Edad);

Estamos usado lo que se llama Encadenar operadores esto quiere decir:

Llamamos a OrderBy desde personas, OrderBy devuelve un IOrderedEnumerable (una enumeración ordenada).

ThenBy es un método de extensión de dicho IOrderedEnumerable así que a eso le aplicamos ThenBy.

Dicho de otra forma, ordenamos primero por nombre y luego ordenamos por edad.

También podemos hacer esto usando una query expression:

IEnumerable<Persona> dobleOrdenacion = from persona in personas orderby persona.Nombre, persona.Edad

select persona;

En ambos casos, el resultado es:

Nombre=García, Javier Edad=24

Nombre=Perez, Juan Edad=15

Nombre=Rodriguez, Jesús Edad=15

Nombre=Rodriguez, Jesús Edad=24

Nombre=Toledo, María Edad=37

Page 41: Operadores de Linq

41 Operadores de LINQ

6.4.3 ThenBy + Comparador personalizado

public static IOrderedEnumerable<TSource> ThenBy<TSource, TKey>( this IOrderedEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer )

Aquí además de la clave, recibimos una instancia de un comparador personalizado.

Vamos a ordenar primero por Edad (de forma descendente) y luego por Nombre pero usando el nombre y no el apellido para ordenar. Para ello usaremos una instancia del comparador adjunto con el código:

IEnumerable<Persona> dobleOrdenacion = personas.OrderByDescending(persona => persona.Edad) .ThenBy(persona => persona.Nombre, new OrdenaNombre());

Como puedes ver, ThenBy trabaja con una enumeración ordenada, pero le da igual si está ordenada ascendentemente o descendentemente.

El resultado es:

Nombre=Toledo, María Edad=37

Nombre=García, Javier Edad=24

Nombre=Rodriguez, Jesús Edad=24

Nombre=Rodriguez, Jesús Edad=15

Nombre=Perez, Juan Edad=15

Page 42: Operadores de Linq

42 6. Ordering (Ordenación)

6.5 ThenByDescending

El operador ThenByDescending toma una enumeración ordenada y le hace una ordenación adicional descendente usando otra clave.

6.5.1 Código necesario para los ejemplos

Una clase persona:

public class Persona { public string Nombre { get; set; } public int Edad { get; set; } public Persona(string nombre, int edad) { Nombre = nombre; Edad = edad; } }

Una lista de personas:

List<Persona> personas = new List<Persona> { new Persona("Rodriguez, Jesús", 24), new Persona("Rodriguez, Jesús", 15), new Persona("Perez, Juan", 15), new Persona("García, Javier", 24), new Persona("Toledo, María", 37) };

Un comparador personalizado:

public class OrdenaNombre : IComparer<string> { public int Compare(string x, string y) { string nombreX = x.Split()[1]; string nombreY = y.Split()[1]; return nombreX.CompareTo(nombreY); } }

Page 43: Operadores de Linq

43 Operadores de LINQ

6.5.2 ThenByDescending estándar

public static IOrderedEnumerable<TSource> ThenByDescending<TSource, TKey>( this IOrderedEnumerable<TSource> source, Func<TSource, TKey> keySelector )

El operador ThenByDescending toma como argumento la clave que se usará para la ordenación adicional (que será descendiente). Devuelve la enumeración ordenada.

Por ejemplo, queremos ordenar la lista de personas primero por su nombre (ascendente) y como ordenación adicional, la edad (descendente):

IEnumerable<Persona> dobleOrdenacion = personas.OrderBy(persona => persona.Nombre).ThenByDescending(persona => persona.Edad);

Estamos usado lo que se llama Encadenar operadores esto quiere decir:

Llamamos a OrderBy desde personas, OrderBy devuelve un IOrderedEnumerable (una enumeración ordenada).

ThenByDescending es un método de extensión de dicho IOrderedEnumerable así que a eso le aplicamos ThenByDescending.

Dicho de otra forma, ordenamos primero por nombre y luego ordenamos por edad.

También podemos hacer esto usando una query expression:

IEnumerable<Persona> dobleOrdenacion = from persona in personas orderby persona.Nombre, persona.Edad descending

select persona;

En ambos casos, el resultado es:

Nombre=García, Javier Edad=24

Nombre=Perez, Juan Edad=15

Nombre=Rodriguez, Jesús Edad=24

Nombre=Rodriguez, Jesús Edad=15

Nombre=Toledo, María Edad=37

Page 44: Operadores de Linq

44 6. Ordering (Ordenación)

6.5.3 ThenByDescending + Comparador personalizado

public static IOrderedEnumerable<TSource> ThenByDescending<TSource, TKey>( this IOrderedEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer )

Aquí además de la clave, recibimos una instancia de un comparador personalizado.

Vamos a ordenar primero por Edad y luego por Nombre (Descendente) pero usando el nombre y no el apellido para ordenar. Para ello usaremos una instancia del comparador adjunto con el código:

IEnumerable<Persona> dobleOrdenacion = personas.OrderBy( persona => persona.Edad).ThenByDescending( persona => persona.Nombre, new OrdenaNombre());

El resultado es:

Nombre=Perez, Juan Edad=15

Nombre=Rodriguez, Jesús Edad=15

Nombre=Rodriguez, Jesús Edad=24

Nombre=García, Javier Edad=24

Nombre=Toledo, María Edad=37

Page 45: Operadores de Linq

45 Operadores de LINQ

7. Grouping (Agrupación)

7.1 GroupBy

El operador GroupBy nos permite agrupar una secuencia bajo una clave.

7.1.1 Código necesario para los ejemplos

Una clase persona:

public class Persona { public string Nombre { get; set;} public int Edad { get; set;} public string Ciudad { get; set; } public Persona(string nombre, int edad, string ciudad) { Nombre = nombre; Edad = edad; Ciudad = ciudad; } }

Una lista de personas:

List<Persona> personas = new List<Persona> { new Persona("Rodriguez, Jesús", 24, "Cádiz"), new Persona("Bautista, Jesús", 15, "Alicante"), new Persona("Perez, Juan", 15, "Cádiz"), new Persona("García, Javier", 24, "Málaga"), new Persona("Toledo, María", 37, "Málaga") };

Un comparador personalizado:

public class MayoriaEdad : IEqualityComparer<int> { public bool Equals(int x, int y) { return (EsMayor(x) == EsMayor(y)); } public int GetHashCode(int edad) { int menor = 1; int mayor = 18;

Page 46: Operadores de Linq

46 7. Grouping (Agrupación)

return (EsMayor(edad) ? mayor.GetHashCode() : menor.GetHashCode());

} public bool EsMayor(int edad) { return (edad >= 18); } }

Un método de ayuda:

public string MayorMenor(int edad) { return edad < 18 ? "Menores de edad" : "Mayores de edad"; }

7.1.2 GroupBy estándar

public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector )

El operador GroupBy estándar simplemente recibe un delegado al que le pasamos la clave que usaremos para agrupar los elementos de la secuencia. Devuelve una enumeración de elementos que tienen una clave común.

Queremos agrupar nuestra lista de personas en grupos donde la clave sea la ciudad. Para ello debemos de indicarle a GroupBy que como clave queremos usar ciudad:

IEnumerable<IGrouping<string, Persona>> personasPorCiudad = personas.GroupBy(persona => persona.Ciudad);

Nada difícil, le decimos que queremos agrupar la secuencia por la ciudad.

También podemos agrupar usando un query expression:

IEnumerable<IGrouping<string, Persona>> personasPorCiudad = from persona in personas group persona by persona.Ciudad into perCiudad select perCiudad;

Por cada persona agrupamos dicha persona por su ciudad en perCiudad y simplemente devolvemos perCiudad.

Page 47: Operadores de Linq

47 Operadores de LINQ

En ambos casos podríamos imprimir el resultado de esta forma:

foreach (var ciudad in personasPorCiudad) { Console.WriteLine(ciudad.Key); foreach (var persona in ciudad) { Console.WriteLine("{0} ({1})", persona.Nombre, persona.Edad); } Console.WriteLine(); }

Y obtener algo así:

Cádiz

Rodriguez, Jesús (24)

Perez, Juan (15)

Alicante

Bautista, Jesús (15)

Málaga

García, Javier (24)

Toledo, María (37)

7.1.3 GroupBy + comparador personalizado

public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer )

Esta sobrecarga hace lo mismo que el GroupBy estándar con la diferencia de que usa un comparador personalizado a la hora de agrupar los elementos.

Page 48: Operadores de Linq

48 7. Grouping (Agrupación)

Hemos creado un comparador personalizado, el cual he adjuntado en el código de este operador, que nos sirve para agrupar las personas por mayores o menores de edad.

IEnumerable<IGrouping<int, Persona>> personasPorMayoria = personas.GroupBy(p => p.Edad, new MayoriaEdad());

Ahora tenemos la secuencia agrupadas por mayores de edad y por menores de edad.

Algo así:

Mayor

24 - Rodriguez, Jesús (Cádiz)

24 - García, Javier (Málaga)

37 - Toledo, María (Málaga)

Menor

15 - Bautista, Jesús (Alicante)

15 - Perez, Juan (Cádiz)

7.1.4 GroupBy + proyector sencillo

public static IEnumerable<IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector )

En esta sobrecarga podemos usar una función para proyectar los elementos de cada grupo.

Page 49: Operadores de Linq

49 Operadores de LINQ

Por ejemplo, vamos a agrupar por la ciudad pero esta vez solo queremos proyectar el nombre, no necesitamos la edad para nada:

IEnumerable<IGrouping<string, string>> perCiudad = personas.GroupBy(p => p.Ciudad, p => p.Nombre);

La diferencia con el operador estándar es que ya no estamos proyectando todo el objeto Persona, sólo estamos proyectando el nombre.

El resultado sería:

Cádiz

Rodriguez, Jesús

Perez, Juan

Alicante

Bautista, Jesús

Málaga

García, Javier

Toledo, María

7.1.5 GroupBy + proyector de enumeración

public static IEnumerable<TResult> GroupBy<TSource, TKey, TResult>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TKey, IEnumerable<TSource>, TResult> resultSelector )

En esta sobrecarga, vamos a agrupar los elementos bajo una clave común pero usamos un segundo delegado el cual recibe la clave, una enumeración con los elementos agrupados bajo dicha clave y devuelve un elemento como resultado. Finalmente el operador devuelve una enumeración de dichos resultados.

Page 50: Operadores de Linq

50 7. Grouping (Agrupación)

Como es algo complicado de entender (y aún más de explicar sin ejemplos) vamos con un ejemplo. Vamos a agrupar por ciudad y lo que queremos saber es cuantas personas de nuestra lista residen en dichas ciudades:

var infoCiudades = personas.GroupBy(p => p.Ciudad, (ciudad, perCiudad) => new { Ciudad = ciudad, NumPersonas = perCiudad.Count() });

Le hemos indicado que queremos agrupar por ciudad, y luego hemos creado un tipo anónimo que contiene la clave y la cantidad de personas por cada ciudad.

Ahora si volvemos a leer la descripción de esta sobrecarga, nos queda más claro. Estamos creando una enumeración de tipos anónimos compuestos por la ciudad y por la cantidad de personas de dicha ciudad.

El resultado es:

Ciudad=Cádiz NumPersonas=2

Ciudad=Alicante NumPersonas=1

Ciudad=Málaga NumPersonas=2

7.1.6 GroupBy + proyector sencillo + comparador personalizado

public static IEnumerable<IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer )

Ésta sobrecarga es una mezcla de 2 sobrecargas que ya hemos visto. Agrupa los elementos dada una clave, proyecta la parte que necesitamos de cada elemento agrupado y para hacer la agrupación.

Así que vamos a hacer una mezcla entre los dos ejemplos de dichas sobrecargas. Vamos a agrupar por mayores o menores de edad y luego

Page 51: Operadores de Linq

51 Operadores de LINQ

de los elementos agrupados, solo vamos a proyectar el nombre (descartando la ciudad y la edad):

IEnumerable<IGrouping<int, string>> porMayoria = personas.GroupBy(p => p.Edad, p => p.Nombre, new MayoriaEdad());

El resultado sería algo así:

Mayor

Rodriguez, Jesús

García, Javier

Toledo, María

Menor

Bautista, Jesús

Perez, Juan

7.1.7 GroupBy + proyector sencillo + proyector de enumeración

public static IEnumerable<TResult> GroupBy<TSource, TKey, TElement, TResult>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, Func<TKey, IEnumerable<TElement>, TResult> resultSelector )

Esta sobrecarga, agrupa los elementos dada una clave, proyecta solo la parte que necesitamos de cada elemento agrupado y por último pasamos cada clave y cada proyección a otro delegado el cual crea un elemento resultante. El operador devuelve una enumeración de dicho elemento.

Page 52: Operadores de Linq

52 7. Grouping (Agrupación)

Vamos a volver a volver a agrupar a las personas por ciudad y vamos a contar cuantas personas hay en cada ciudad:

var perCiudad = personas.GroupBy(p => p.Ciudad, p => p.Ciudad, (key, ciudades) => new { Ciudad = key, NumPersonas = ciudades.Count() });

El ejemplo es el mismo que hemos usado en la sobrecarga del proyector de enumeración. La diferencia es que aquí en vez de pasarle una enumeración de personas le he pasado una enumeración de cadenas.

La idea es que como no necesitamos toda la información que nos provee la clase persona, proyectamos simplemente aquella parte que necesitamos (aunque puntualizando, aquí realmente no necesitamos ni la edad, ni el nombre ni la ciudad, pero tenemos que elegir al menos un elemento a proyectar).

El resultado es el mismo:

Ciudad=Cádiz NumPersonas=2

Ciudad=Alicante NumPersonas=1

Ciudad=Málaga NumPersonas=2

7.1.8 GroupBy + proyector de enumeración + comparador personalizado

public static IEnumerable<TResult> GroupBy<TSource, TKey, TResult>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TKey, IEnumerable<TSource>, TResult> resultSelector, IEqualityComparer<TKey> comparer )

Esta sobrecarga tiene el delegado de enumeración que ya hemos visto en dos ocasiones y además soporta un comparador personalizado.

Page 53: Operadores de Linq

53 Operadores de LINQ

Vamos a agrupar por mayoría o minoría de edad (usando nuestro comparador) y luego vamos a proyectar la cantidad de personas en cada grupo:

var infoEdades = personas.GroupBy(p => p.Edad, (edad, pers) => new { Condicion = MayorMenor(edad), NumPersonas = pers.Count() }, new MayoriaEdad());

Bueno, nada nuevo realmente por aquí. El comparador siempre usa la clave para comparar y simplemente sacamos información de cada grupo.

El resultado es:

Mayores de edad

Numero personas: 3

Menores de edad

Numero personas: 2

7.1.9 GroupBy + proyector simple + proyector de enumeración + comparador personalizado

public static IEnumerable<TResult> GroupBy<TSource, TKey, TElement, TResult>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, Func<TKey, IEnumerable<TElement>, TResult> resultSelector, IEqualityComparer<TKey> comparer )

He aquí la combinación de todas las sobrecargas de este pequeño operador. Tiene su proyector de elementos, proyecta luego una enumeración y todo con su comparador personalizado.

Page 54: Operadores de Linq

54 7. Grouping (Agrupación)

Vamos a agrupar por mayoría o minoría de edad, y vamos a sacar varios datos sobre dichas edades:

var infoEdades = personas.GroupBy(p => p.Edad, p => p.Edad, (edad, edades) => new { Condicion = MayorMenor(edad), Min = edades.Min(), Max = edades.Max() }, new MayoriaEdad());

Agrupamos por edad, proyectamos solo la edad puesto que es lo único que necesitamos, guardamos la edad mínima y máxima del grupo y usamos el comparador personalizado para hacer las agrupaciones.

El resultado es:

Mayores de edad

Edad mínima: 24

Edad máxima: 37

Menores de edad

Edad mínima: 15

Edad máxima: 15

Page 55: Operadores de Linq

55 Operadores de LINQ

7.2 ToLookup

El operador ToLookup es bastante parecido a GroupBy en cuanto uso, la diferencia es que este operador crea un objeto del tipo ToLookup como veremos en los ejemplos.

7.2.1 Código necesario para los ejemplos

Una clase persona:

public class Persona { public string Nombre { get; set;} public int Edad { get; set;} public string Ciudad { get; set; } public Persona(string nombre, int edad, string ciudad) { Nombre = nombre; Edad = edad; Ciudad = ciudad; } }

Una lista de personas:

List<Persona> personas = new List<Persona> { new Persona("Rodriguez, Jesús", 24, "Cádiz"), new Persona("Bautista, Jesús", 15, "Alicante"), new Persona("Perez, Juan", 15, "Cádiz"), new Persona("García, Javier", 24, "Málaga"), new Persona("Toledo, María", 37, "Málaga") };

Un comparador personalizado:

public class MayoriaEdad : IEqualityComparer<int> { public bool Equals(int x, int y) { return (EsMayor(x) == EsMayor(y)); } public int GetHashCode(int edad) { int menor = 1; int mayor = 18;

Page 56: Operadores de Linq

56 7. Grouping (Agrupación)

return (EsMayor(edad) ? mayor.GetHashCode() : menor.GetHashCode());

} public bool EsMayor(int edad) { return (edad >= 18); } }

7.2.2 ToLookup estándar

public static ILookup<TKey, TSource> ToLookup<TSource, TKey>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector )

El operador ToLookup recibe un delegado que usaremos para seleccionar la clave. Este operador devuelve un objeto que implemente la interfaz ILookup<TKey, TSource>.

ILookup implementa a su vez IEnumerable<IGrouping<TKey, TElement>>. Lo cual podemos deducir que este operador hace lo que hace GroupBy pero con funcionalidad extra. La idea está en que este operador nos crea un objeto al cual podemos hacerle consultas. Para que quede más claro vamos con un ejemplo:

ILookup<string, Persona> perCiudad = personas.ToLookup( p => p.Ciudad);

Aquí creamos un objeto que implementa la interfaz ILookup. En este caso la clave es una cadena (la ciudad) y como elemento contiene una enumeración de personas (que vivan en esa ciudad).

¿Es lo mismo que GroupBy, no? No exactamente, Imaginemos que necesitamos las personas que sean de Cádiz, pues ahora podemos hacer:

IEnumerable<Persona> gaditanos = perCiudad["Cádiz"];

Como podéis ver, el objeto tiene un indizador, así que podemos acceder aquellos elementos que queramos simplemente especificando la clave.

Imprimir esa variable daría algo del tipo:

Nombre=Rodriguez, Jesús Edad=24 Ciudad=Cádiz

Nombre=Perez, Juan Edad=15 Ciudad=Cádiz

Page 57: Operadores de Linq

57 Operadores de LINQ

7.2.3 ToLookup + comparador personalizado

public static ILookup<TKey, TSource> ToLookup<TSource, TKey>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer )

Esta sobrecarga toma además un comparador personalizado para seleccionar la clave. Veámoslo con un ejemplo:

ILookup<int, Persona> mayorOMenor = personas.ToLookup(p => p.Edad, new MayoriaEdad());

Ésta es la parte sencilla de entender. Creamos un objeto que implementa ILookup y para comparar las claves usamos nuestro comparador personalizado.

Ahora la parte difícil de entender:

IEnumerable<Persona> mayores = mayorOMenor[20];

No hay nadie con 20 años, ¿no? Es cierto, pero no es lo que le estamos pidiendo.

Como sabréis, la mayoría de operadores de LINQ implementan la ejecución diferida. ¿Qué significa esto? Que cuando ejecutas el operador no estás realmente creando ninguna colección ni nada. Lo que obtienes del operador es un objeto que almacena la información necesaria para realizar la acción de dicho operador.

Así que cuando ejecutamos la sentencia anterior, lo que va a hacer es enviar el valor del indizador (en nuestro caso 20) al comparador, ahí va a compararlo con todas las edades. Algo así:

Toma la edad que le hemos pasado (20) y comprueba que es una edad sobre 18. Luego va tomando cada valor de la lista. Por ejemplo 15, no es mayor de edad así que descartada, luego coge 24, también es mayor de edad, y como ambas lo son, devuelve la persona con dicha edad.

En resumen, con esta sobrecarga, puedes pasarle cualquier número al objeto ILookup que si es mayor de 18 (mayoría de edad) devolverá todos los mayores de edad, y si es un número menor a 18, devolverá aquellos que no lo son. Con la otra sobrecarga solo podías meterle valores específicos para que funcionase.

Page 58: Operadores de Linq

58 7. Grouping (Agrupación)

Así que si le pasamos 20 conseguiremos:

Nombre=Rodriguez, Jesús Edad=24 Ciudad=Cádiz

Nombre=García, Javier Edad=24 Ciudad=Málaga

Nombre=Toledo, María Edad=37 Ciudad=Málaga

7.2.4 ToLookup + proyector

public static ILookup<TKey, TElement> ToLookup<TSource, TKey, TElement>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector )

En esta sobrecarga tenemos la posibilidad de definir como serán los elementos asociados a las claves.

Volviendo al ejemplo de agrupar por ciudades. Nosotros agrupábamos por ciudad, luego le decíamos que queríamos todas aquellas personas que fueran de Cádiz. El "problema" es que cada objeto que nos devuelve también contiene una propiedad del tipo Ciudad lo cual es redundante, puesto que ya sabemos que son de Cádiz.

Podríamos solucionar eso de la siguiente forma:

var perCiudad = personas.ToLookup(p => p.Ciudad, p => new { p.Nombre, p.Edad });

Hemos creado un objeto que implementa ILookup pero esta vez, aparte de agrupar por ciudad, hemos creado un tipo anónimo que solo contiene el nombre y la edad descartando la ciudad (pues ya la sabemos a la hora de hacer una consulta).

Se usaría como siempre:

var gaditanos = perCiudad["Cádiz"];

Y el resultado:

Nombre=Rodriguez, Jesús Edad=24

Nombre=Perez, Juan Edad=15

Page 59: Operadores de Linq

59 Operadores de LINQ

7.2.5 ToLookup + proyector + comparador personalizado

public static ILookup<TKey, TElement> ToLookup<TSource, TKey, TElement>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer )

Esta sobrecarga es una mezcla entre las 3 anteriores. Agrupamos dada una clave, proyectamos aquello que necesitamos y usamos un comparador personalizado para la agrupación.

Vamos a agrupar por edad como hicimos antes, proyectamos solo nombre - ciudad y usamos nuestro comparador:

var mayorOMenor = personas.ToLookup(p => p.Edad, p => new { p.Nombre, p.Ciudad }, new MayoriaEdad());

Y pedimos a todos los menores (se puede poner cualquier edad que sea menor a 18):

var menores = mayorOMenor[17];

El resultado es:

Nombre=Bautista, Jesús Ciudad=Alicante

Nombre=Perez, Juan Ciudad=Cádiz

Page 60: Operadores de Linq

60 8. Set (Conjunto)

8. Set (Conjunto) 8.1 Distinct

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

8.1.1 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(); } }

8.1.2 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 implícito. Devuelve una enumeración donde ya no hay entradas repetidas.

Veámoslo en un ejemplo:

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

Page 61: Operadores de Linq

61 Operadores de LINQ

¿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 :).

8.1.3 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

Page 62: Operadores de Linq

62 8. Set (Conjunto)

8.2 Except

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

8.2.1 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"};

8. Set (Conjunto)

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(); } }

8.2.2 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.

Page 63: Operadores de Linq

63 Operadores de LINQ

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. Veámoslo 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, simplemente 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

8.2.3 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 analicémoslo:

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...

Recordad cómo trabaja el operador.

Page 64: Operadores de Linq

64 8. Set (Conjunto)

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).

8.3 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.

8.3.1 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" };

Page 65: Operadores de Linq

65 Operadores de LINQ

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(); } }

8.3.2 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 difícil, el resultado es:

Cádiz

Málaga

Bilbao

Esas tres ciudades coinciden en ambas listas :)

Page 66: Operadores de Linq

66 8. Set (Conjunto)

8.3.3 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 difícil encontrar ejemplos simples) que nos va ayudar a entender cómo funciona este operador.

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

¿Qué debería de devolver? Piénsalo 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 sí, 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.

Page 67: Operadores de Linq

67 Operadores de LINQ

8.4 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.

8.4.1 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(); } }

8.4.2 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.

Page 68: Operadores de Linq

68 8. Set (Conjunto)

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

8.4.3 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.

Page 69: Operadores de Linq

69 Operadores de LINQ

El resultado es:

Cádiz

Málaga

Barcelona

Galicia

Pamplona

Page 70: Operadores de Linq

70 9. Conversion (Conversión)

9. Conversion (Conversión)

9.1 AsEnumerable

El operador AsEnumerable convierte una secuencia en una enumeración del tipo IEnumerable<T>.

9.1.1 Código necesario para los ejemplos

Un objeto del tipo IQueryable como resultado de un LINQ to SQL:

IQueryable<Persona> personas = db.Persona;

NOTA: db es una instancia de un System.Data.Linq.DataContext o en otras palabras, una instancia del LINQ to SQL. Es un ejemplo imaginario para simplificar el ejemplo.

9.1.2 AsEnumerable

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

Éste operador puede parecer extraño, absurdo. Un operador que quiere transformar una secuencia a un IEnumerable<T> y para ello necesita un... ¿IEnumerable<T>? Los operadores estándar son los de LINQ to Objects y esos son miembros de IEnumerable<T>. La cosa es que hay distintas implementaciones como por ejemplo LINQ to SQL. LINQ to SQL trabaja con IQueryable y éste implementa sus propios operadores. Teniendo esto en consideración, IQueryable puede tener operadores como... Where, Select, etc. La cosa es que están implementados de otra forma puesto que trabajan con otros tipos de datos. No solo eso, puede que no implemente ciertos operadores que nosotros necesitamos usar. ¿Qué podemos hacer? Si, lo que estáis pensando. Podemos convertir nuestra secuencia IQueryable<T> por una IEnumerable<T> y ya poder usar esos operadores que faltan o las implementaciones de IEnumerable<T> de los operadores.

Page 71: Operadores de Linq

71 Operadores de LINQ

¿Cómo? Así:

IEnumerable<Persona> ienupersonas = personas.AsEnumerable();

Con esto hemos convertido de IQueryable<T> a IEnumerable<T>. Hemos podido usar ese método en el IQueryable<T> puesto que esta interfaz implementa IEnumerable<T>.

9.2 AsQueryable

El operador AsQueryable convierte una enumeración del tipo IEnumerable en una del tipo IQueryable<T>

9.2.1 Código necesario para los ejemplos

Una tabla de nuestra base de datos convertida a IEnumerable

IQueryable<Persona> personas = db.Persona; IEnumerable<Persona> ienupersonas = personas.AsEnumerable();

9.2.2 AsQueryable

public static IQueryable<TElement> AsQueryable<TElement>( this IEnumerable<TElement> source )

Éste método es justo lo contrario de AsEnumerable. Recomendaría leer su descripción para mejor comprensión. Básicamente, si tienes un objeto del tipo IEnumerable como por ejemplo el de nuestro código adjunto, puedes necesitar convertirlo a IQueryable. Por ejemplo, tenemos un IQueryable proveniente de una tabla de nuestra base de datos. La convertimos a IEnumerable como ya se explicó. Hacemos el trabajo que tengamos que hacer y luego la volvemos a convertir a IQueryable. Para hacer esto último, hacemos un:

IQueryable<Persona> iquerypersonas = ienupersonas.AsQueryable();

Podríamos haber reusado el objeto personas pero era para dar otro ejemplo.

Page 72: Operadores de Linq

72 9. Conversion (Conversión)

9.3 Cast

El operador Cast puede convertir un array estándar a un objeto que implementa IEnumerable<T>.

9.3.1 Código necesario para los ejemplos

Un array de enteros:

int[] arrayNum = { 1, 2, 3, 4, 5 };

9.3.2 Cast

public static IEnumerable<TResult> Cast<TResult>( this IEnumerable source )

El operador Cast se usa con la secuencia que queremos convertir a un tipo. Devolverá dicha secuencia convertida a una enumeración de dicho tipo. Por ejemplo, queremos convertir nuestro array de enteros a una enumeración de enteros:

IEnumerable<int> enuNum = arrayNum.Cast<int>();

El resultado es:

1 2 3 4 5

Ahora la pregunta es: ¿Para qué queremos convertir el array a un IEnumerable<int>? Hay una razón sencilla: Un array normal y corriente no implementa IEnumerable<T> lo que conlleva a no poder usar ningún operador de LINQ. Así que si convertimos nuestro array en un IEnumerable<T> pues podremos aplicar algún que otro operador, por ejemplo:

IEnumerable<int> enuNum = arrayNum.Cast<int>().Select(i => i * i);

Ahora gracias a Cast hemos podido aplicarle un Select.

Page 73: Operadores de Linq

73 Operadores de LINQ

El resultado de esto último sería:

1 4 9 16 25

9.4 ToArray

El operador ToArray convierte una enumeración del tipo IEnumerable<T> en un array.

9.4.1 Código necesario para los ejemplos

Una clase persona:

public class Persona { public string Nombre { get; set;} public int Edad { get; set;} public string Ciudad { get; set; } public Persona(string nombre, int edad, string ciudad) { Nombre = nombre; Edad = edad; Ciudad = ciudad; } }

Una lista de personas:

List<Persona> personas = new List<Persona> { new Persona("Rodriguez, Jesús", 24, "Cádiz"), new Persona("Bautista, Jesús", 15, "Alicante"), new Persona("Perez, Juan", 15, "Cádiz"), new Persona("García, Javier", 24, "Málaga"), new Persona("Toledo, María", 37, "Málaga") };

Page 74: Operadores de Linq

74 9. Conversion (Conversión)

9.4.2 ToArray

public static TSource[] ToArray<TSource>( this IEnumerable<TSource> source )

Éste operador toma como parámetro un objeto IEnumerable<T> de un tipo y devuelve un array de dicho tipo. Por ejemplo, tenemos una lista de personas, queremos crear un array con todos los nombres de las personas. Lo haré en dos pasos para verlo mejor

IEnumerable<string> nombres = personas.Select(p => p.Nombre); string[] arrayNombres = nombres.ToArray();

Como ves, proyecto solo los nombres en una enumeración de string y luego convierto dicha enumeración en un array de string. Se puede reducir a un paso también:

string[] nombres = personas.Select(p => p.Nombre).ToArray();

En ambos casos, se imprimirían los nombres:

Rodriguez, Jesús Bautista, Jesús Perez, Juan García, Javier Toledo, María

9.5 ToDictionary

El operador ToDictionary convierte una enumeración del tipo IEnumerable en un Dictionary<TKey, TValue>.

9.5.1 Código necesario para los ejemplos

Una clase persona:

public class Persona { public string Nombre { get; set;} public string Edad { get; set;} public string Ciudad { get; set; }

Page 75: Operadores de Linq

75 Operadores de LINQ

public Persona(string nombre, string edad, string ciudad) { Nombre = nombre; Edad = edad; Ciudad = ciudad; } }

Una lista de personas:

List<Persona> personas = new List<Persona> { new Persona("Rodriguez, Jesús", "24", "Cádiz"), new Persona("Bautista, Jesús", "15", "Alicante"), new Persona("Perez, Juan", "16", "Cádiz"), new Persona("García, Javier", "26", "Málaga"), new Persona("Toledo, María", "37", "Málaga") };

Un comparador personalizado:

public class StringANumero : IEqualityComparer<string> { public bool Equals(string x, string y) { return (Int32.Parse(x) == Int32.Parse(y)); } public int GetHashCode(string obj) { return Int32.Parse(obj).ToString().GetHashCode(); } }

9.5.2 ToDictionary estándar

public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector )

ToDictionary recibe un delegado que usaremos para seleccionar la clave. Devuelve un diccionario cuyas claves son las que hemos seleccionado e irán con su valor correspondido. A diferencia de ToLookup en un diccionario las claves van asociadas a un solo valor. Las claves no se pueden repetir.

Page 76: Operadores de Linq

76 9. Conversion (Conversión)

Por ejemplo, queremos transformar nuestra lista de personas a un diccionario donde la clave sea el nombre:

Dictionary<string, Persona> dictNombres = personas.ToDictionary( p => p.Nombre);

Y tal como pasa con ILookup podemos hacer un:

Persona jesus = dictNombres["Rodriguez, Jesús"];

9.5.3 ToDictionary + comparador personalizado

public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer )

Con esta sobrecarga podemos crear un diccionario a partir de una clave, pero ahora usaremos un comparador personalizado para comparar las claves. Vamos a verlo:

Dictionary<string, Persona> dictEdad = personas.ToDictionary( p => p.Edad, new StringANumero());

Nada nuevo, usamos nuestro comparador. Ahora bien, quiero recoger del diccionario la persona de 24 años, o sea, Jesús:

Persona jesus = dictEdad["000024"];

Ea, ahí está... ¿Pero por qué funciona? Como ya vimos en ToLookup, cuando le pedimos al diccionario que nos devuelva el valor asociado a una clave (en este caso "000024") lo que hace es buscar dicha clave en el diccionario comparando la clave que le pasamos con cada clave del diccionario. Al usar un comparador personalizado, esas comparaciones se hacen a través de nuestro comparador personalizado. Entonces, el método Parse del de la clase Int32 lo que hace es convertir nuestra cadena "000024" a un Int32 para ello quita los ceros de la izquierda y quedaría simplemente como 24, claro, cuando llega a la entrada de Jesús ve que su edad es 24 también y la da por válida.

Page 77: Operadores de Linq

77 Operadores de LINQ

9.5.4 ToDictionary + proyector elemento

public static Dictionary<TKey, TElement> ToDictionary<TSource, TKey, TElement>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector )

Ahora podemos definir cuál será la clave y como serán los elementos gracias al delegado que nos permite proyectar lo que necesitemos de cada elemento. Vamos a crear un diccionario con el nombre como clave, pero solo nos vamos a quedar con la ciudad, nada más:

Dictionary<string, string> dictNombre = personas.ToDictionary( p => p.Nombre, p => p.Ciudad);

Si imprimiéramos el diccionario, conseguiríamos algo así:

[Rodriguez, Jesús, Cádiz] [Bautista, Jesús, Alicante] [Perez, Juan, Cádiz] [García, Javier, Málaga] [Toledo, María, Málaga]

9.5.5 ToDictionary + proyector elemento + comparador personalizado

public static Dictionary<TKey, TElement> ToDictionary<TSource, TKey, TElement>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer )

Además de la clave y el proyector para los elementos, podemos usar un comparador personalizado para comparar las claves. Vamos a mejorar el ejemplo anterior creando un diccionario con la edad como clave pero solo necesitamos el nombre de la persona y nada más:

Dictionary<string, string> dictEdad = personas.ToDictionary( p => p.Edad, p => p.Nombre, new StringANumero());

Ahora por cada clave (edad) solo habrá una cadena con el nombre y no todo el objeto Persona entero. Y cada vez que le pedimos al diccionario

Page 78: Operadores de Linq

78 9. Conversion (Conversión)

que nos devuelva un nombre, comprobará cada edad con nuestro comparador personalizado.

9.6 ToList

El operador ToList convierte una enumeración del tipo IEnumerable<T> en un objeto del tipo List<T>.

9.6.1 Código necesario para los ejemplos

Una clase persona:

public class Persona { public string Nombre { get; set;} public int Edad { get; set;} public string Ciudad { get; set; } public Persona(string nombre, int edad, string ciudad) { Nombre = nombre; Edad = edad; Ciudad = ciudad; } }

Una lista de personas:

List<Persona> personas = new List<Persona> { new Persona("Rodriguez, Jesús", 24, "Cádiz"), new Persona("Bautista, Jesús", 15, "Alicante"), new Persona("Perez, Juan", 15, "Cádiz"), new Persona("García, Javier", 24, "Málaga"), new Persona("Toledo, María", 37, "Málaga") };

9.6.2 ToList

public static List<TSource> ToList<TSource>( this IEnumerable<TSource> source )

Recibe como parámetro el objeto tipo IEnumerable<T> a convertir. Devuelve un objeto List de dicho tipo.

Page 79: Operadores de Linq

79 Operadores de LINQ

Por ejemplo, creamos una enumeración proyectando solo los nombres de las personas, luego lo ordenamos y por último lo convertimos a una lista:

IEnumerable<string> orderYSelect = personas.OrderBy( p => p.Nombre).Select(p => p.Nombre);

List<string> nombresOrdenados = orderYSelect.ToList();

O lo que es lo mismo:

List<string> nombresOrdenados = personas.OrderBy( p => p.Nombre).Select(p => p.Nombre).ToList();

El resultado de imprimir esto sería:

Bautista, Jesús García, Javier Perez, Juan Rodriguez, Jesús Toledo, María

Page 80: Operadores de Linq

80 10. Equality (Igualdad)

10. Equality (Igualdad)

10.1 SequenceEqual

El operador SequenceEqual sirve para comprobar la igualdad entre dos secuencias

10.1.1 Código necesario para los ejemplos

Dos listas con el mismo contenido:

List<string> lista1 = new List<string> { "Abuelo", "Madre", "Tio", "Sobrina" }; List<string> lista2 = new List<string> { "Abuelo", "Madre", "Tio", "Sobrina" };

Una lista con contenido distinto:

List<string> listaDis = new List<string> { "Agencia", "Mina", "Tronco", "Sandalia" };

Un comparador personalizado:

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(); } }

10.1.2 SequenceEqual estándar

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

Una secuencia llamará a este operador pasando una segunda secuencia como parámetro. Se comparará cada elemento de la secuencia usando el comparador por defecto. Devolverá un bool indicándonos si son iguales o no las secuencias.

Page 81: Operadores de Linq

81 Operadores de LINQ

Por ejemplo, vamos a comprar las dos listas que tenemos en con el mismo contenido:

bool igual = lista1.SequenceEqual(lista2);

Irá comparando cada elemento usando el comparador por defecto de dicho elemento, en este caso el comparador de las cadenas. Como las listas tienen el mismo contenido, pues dará:

true

¿Qué pasa si la comparamos con la lista distinta?

bool igual = lista1.SequenceEqual(listaDis);

Auch, ahora el resultado es:

false

Aunque no debería de sorprendernos.

10.1.3 SequenceEqual + comparador personalizado

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

Ahora podremos comparar los elementos de las secuencias usando un comparador personalizado. Vamos a comparar usando solo la inicial:

bool igual = lista1.SequenceEqual(listaDis, new ComparadorInicial());

Aquí comparará los elementos de la lista1 y de la lista2 usando nuestro comparador personalizado. En otras palabras comprobará la inicial de cada palabra, y como coinciden, el resultado es:

true

Page 82: Operadores de Linq

82 11. Element (Elemento)

11. Element (Elemento)

11.1 ElementAt

Con el operador ElementAt podemos extraer un elemento de una secuencia dado su índice.

11.1.1 Código necesario para los ejemplos

Una lista de lenguajes:

List<string> lenguajes = new List<string> { "C#", "Visual Basic", "Python", "PHP", "C" };

11.1.2 ElementAt

public static TSource ElementAt<TSource>( this IEnumerable<TSource> source, int index )

El operador toma un int que será el índice (empezado desde 0) del elemento que queremos extraer. Si quisiéramos extraer el lenguaje Python, el índice sería el 2:

string python = lenguajes.ElementAt(2);

Lo que imprimiría:

Python

11.2 ElementAtOrDefault

El operador ElementAtOrDefault trabaja igual que su hermano ElementAt sólo que devolverá un valor predeterminado si no se encuentra dicho elemento.

Page 83: Operadores de Linq

83 Operadores de LINQ

11.2.1 Código necesario para los ejemplos

Una lista de lenguajes:

List<string> lenguajes = new List<string> { "C#", "Visual Basic", "Python", "PHP", "C" };

11.2.2 ElementAtOrDefault

public static TSource ElementAtOrDefault<TSource>( this IEnumerable<TSource> source, int index )

El operador toma un int que será el índice (empezando desde 0) del elemento a extraer. Si dicho en dicho índice no existiera un elemento, devolverá el valor por defecto del tipo. Por ejemplo, si queremos extraer un string pero resulta que en ese índice no hay un string, devolvería el valor por defecto del tipo string. Lo mismo con cualquier otro tipo. Así que por ejemplo:

string nada = lenguajes.ElementAtOrDefault(20);

No daría una excepción de fuera de rango, simplemente nada sería null.

11.3 First

El operador First devuelve el primer elemento de una secuencia.

11.3.1 Código necesario para los ejemplos

Una lista de lenguajes:

List<string> lenguajes = new List<string> { "C#", "Visual Basic", "Python", "PHP", "C" };

11.3.2 First estándar

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

First no toma ningún parámetro. Simplemente ejecutarlo en la secuencia en la que queramos extraer el primer elemento.

Page 84: Operadores de Linq

84 11. Element (Elemento)

¿Queremos el primer elemento de la secuencia? Fácil:

string csharp = lenguajes.First();

Como es obvio, la salida es:

C#

11.3.3 First + condición

public static TSource First<TSource>( this IEnumerable<TSource> source, Func<TSource, bool> predicate )

Ésta sobrecarga toma un delegado el cual usaremos como condición. ¿Condición para qué? Para extraer el primer elemento que cumpla dicha condición. Por ejemplo, queremos el primer elemento de la secuencia que tenga un solo carácter. ¿No iba a ser siempre el primer elemento y ya está, no?:

string unChar = lenguajes.First(e => e.Length == 1);

¿Cuál es el primer elemento de la secuencia con un solo carácter?:

C

11.4 FirstOrDefault

El operador FirstOrDefault devuelve el primer elemento de una secuencia o un valor predeterminado si no encuentra ningún elemento.

11.4.1 Código necesario para los ejemplos

Una lista de lenguajes:

List<string> lenguajes = new List<string> { "C#", "Visual Basic", "Python", "PHP", "C" };

Una lista vacía:

List<int> listaVacia = new List<int>();

Page 85: Operadores de Linq

85 Operadores de LINQ

11.4.2 FirstOrDefault estándar

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

FirstOrDefault devuelve el primer elemento de una secuencia o un valor predefinido si no hay elementos. Por ejemplo, queremos el primer elemento de la lista vacía:

int primero = listaVacia.FirstOrDefault();

Al estar vacía, devuelve un valor predefinido. ¿Cuál es el valor por defecto de los int?:

0

11.4.3 FirstOrDefault + condición

public static TSource FirstOrDefault<TSource>( this IEnumerable<TSource> source, Func<TSource, bool> predicate )

Ahora tenemos la posibilidad de elegir el primer elemento de una secuencia que cumpla una cierta condición. Si ningún elemento cumple dicha condición, se devolverá un valor predefinido. Queremos el primer lenguaje de la lista con 5 caracteres:

string cinco = lenguajes.FirstOrDefault(e => e.Length == 5);

Al no existir ninguno, se devuelve el valor por defecto de los string, o sea, null

11.5 Last

El operador Last devuelve el último elemento de una secuencia.

11.5.1 Código necesario para los ejemplos

Una lista de lenguajes:

List<string> lenguajes = new List<string> { "C#", "Visual Basic", "Python", "PHP", "C" };

Page 86: Operadores de Linq

86 11. Element (Elemento)

11.5.2 Last estándar

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

Last no toma ningún parámetro. Simplemente ejecutarlo en la secuencia en la que queramos extraer el último elemento. ¿Queremos el último elemento de la secuencia? Fácil:

string c = lenguajes.Last();

Como es obvio, la salida es:

C

11.5.3 Last + condición

public static TSource Last<TSource>( this IEnumerable<TSource> source, Func<TSource, bool> predicate )

Ésta sobrecarga toma un delegado el cual usaremos como condición. ¿Condición para qué? Para extraer el último elemento que cumpla dicha condición. Por ejemplo, queremos el último elemento de la secuencia que tenga 3 o más caracteres.

string tresChar = lenguajes.Last(e => e.Length >= 3);

¿Cuál es el último elemento de la secuencia con 3 o más caracteres?:

PHP

Page 87: Operadores de Linq

87 Operadores de LINQ

11.6 LastOrDefault

El operador LastOrDefault devuelve el último elemento de una secuencia o un valor predeterminado si no encuentra ningún elemento.

11.6.1 Código necesario para los ejemplos

Una lista de lenguajes:

List<string> lenguajes = new List<string> { "C#", "Visual Basic", "Python", "PHP", "C" };

Una lista vacía:

List<int> listaVacia = new List<int>();

11.6.2 LastOrDefault estándar

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

LastOrDefault devuelve el último elemento de una secuencia o un valor predefinido si no hay elementos. Por ejemplo, queremos el último elemento de la lista vacía:

int ultimo = listaVacia.LastOrDefault();

Al estar vacía, devuelve un valor predefinido. ¿Cuál es el valor por defecto de los int?:

0

11.6.3 LastOrDefault + condición

public static TSource LastOrDefault<TSource>( this IEnumerable<TSource> source, Func<TSource, bool> predicate )

Ahora tenemos la posibilidad de elegir el último elemento de una secuencia que cumpla una cierta condición. Si ningún elemento cumple dicha condición, se devolverá un valor predefinido.

Page 88: Operadores de Linq

88 11. Element (Elemento)

Queremos el último lenguaje de la lista que empiece por Z

string zeta = lenguajes.LastOrDefault(e => e.StartsWith("Z"));

Al no existir ninguno, se devuelve el valor por defecto de los string, o sea, null.

11.7 Single

El operador Single devuelve el único elemento de una secuencia. Si hay más de un elemento dará una excepción. Si no hay elementos también dará excepción.

11.7.1 Código necesario para los ejemplos

Una lista con un solo elemento:

List<int> unEle = new List<int> { 24 };

Una lista de lenguajes:

List<string> lenguajes = new List<string> { "C#", "Visual Basic", "Python", "PHP", "C" };

11.7.2 Single estándar

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

Este operador se usa sobre una secuencia que contenga un solo elemento. Por ejemplo:

int numero = unEle.Single();

En este caso nada raro, devuelve:

24

Lo que tiene este operador es que si la lista contiene más de un elemento (o ninguno), dará una excepción.

Page 89: Operadores de Linq

89 Operadores de LINQ

11.7.3 Single + condición

public static TSource Single<TSource>( this IEnumerable<TSource> source, Func<TSource, bool> predicate )

Ahora podemos establecer una condición. Devolverá el elemento que cumpla dicha condición. ¡Pero atención! Si hay más de un elemento que cumpla dicha condición dará una excepción. También la dará si ninguno la cumple. Queremos el lenguaje de 2 caracteres:

string csharp = lenguajes.Single(e => e.Length == 2);

Como solo existe uno, no tira ninguna excepción:

C#

11.8 SingleOrDefault

El operador SingleOrDefault devuelve el único elemento de una secuencia o un valor predeterminado si está vacía. Da una excepción si hay más de un elemento.

11.8.1 Código necesario para los ejemplos

Una lista de lenguajes:

List<string> lenguajes = new List<string> { "C#", "Visual Basic", "Python", "PHP", "C" };

Una lista vacía:

List<int> listaVacia = new List<int>();

11.8.2 SingleOrDefault estándar:</u></b>

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

Este operador se usa sobre una secuencia. Si esta tiene un elemento devolverá dicho elemento. En caso de que esté vacía, devolverá un valor predefinido. Si tiene más de un elemento lanzará una excepción.

Page 90: Operadores de Linq

90 11. Element (Elemento)

Por ejemplo, queremos el valor de la lista vacía:

int vacia = listaVacia.SingleOrDefault();

Como no existe tal elemento, devolverá:

0

O sea, el valor por defecto de int.

11.8.3 SingleOrDefault + condición

public static TSource SingleOrDefault<TSource>( this IEnumerable<TSource> source, Func<TSource, bool> predicate )

Este operador se usa sobre una secuencia. Devolverá el elemento que cumpla la condición especificada. En caso de no encontrar ninguno devolverá un valor predefinido. Si tiene más de un elemento que cumpla dicha condición, lanzará una excepción. Queremos el elemento de 4 caracteres:

string cuatroChar = lenguajes.SingleOrDefault(e => e.Length == 4);

¡Ah! Que no hay... Bueno, entonces devolverá null que es el valor predefinido de los string.

Page 91: Operadores de Linq

91 Operadores de LINQ

12. Generation (Generación)

12.1 DefaultIfEmpty

El operador DefaultIfEmpty devuelve una secuencia o un valor predefinido si dicha secuencia está vacía.

12.1.1 Código necesario para los ejemplos

Una lista de lenguajes: List<string> lenguajes = new List<string> { "C#", "Visual Basic", "Python", "PHP", "C" };

Una lista vacía:

List<int> listaVacia = new List<int>();

12.1.2 DefaultIfEmpty estándar

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

El operador se usa sobre una secuencia. Si la secuencia contiene elementos pues devolverá dichos elementos sin tocar. Si no hay elementos, devolverá el valor predefinido. Por ejemplo, si lo usamos en la lista de lenguajes:

IEnumerable<string> llena = lenguajes.DefaultIfEmpty();

Al no estar vacía, simplemente devolverá todos y cada uno de los elementos:

C# Visual Basic Python PHP C

¿Pero qué pasa si la usamos en una vacía?:

IEnumerable<int> vacia = listaVacia.DefaultIfEmpty();

Page 92: Operadores de Linq

92 12. Generation (Generación)

Que al no haber elementos, devolverá el valor predefinido, o sea:

0

12.1.3 DefaultIfEmpty + valor predefinido

public static IEnumerable<TSource> DefaultIfEmpty<TSource>( this IEnumerable<TSource> source, TSource defaultValue )

¿Podríamos cambiar el valor predefinido por uno más útil para nuestra aplicación? Con esta sobrecarga podemos definir cuál será dicho valor. Por ejemplo, en nuestra aplicación usamos el número -1 para indicar que una lista está vacía (Siempre es mejor usar una enumeración en estos casos):

IEnumerable<int> vacia = listaVacia.DefaultIfEmpty(-1);

Simplemente tenemos que pasarle dicho valor predefinido al operador y lo usará si la secuencia está vacía. Obviamente el contenido de vacia es:

-1

12.2 Empty

El operador Empty crea una enumeración de un tipo dado.

12.2.1 Empty

public static IEnumerable<TResult> Empty<TResult>()

Éste operador no se usa en ninguna secuencia, se usa para crear una enumeración de un tipo dado por el operador. Por ejemplo, si queremos una enumeración de cadenas podemos hacer:

IEnumerable<string> cadenas = Enumerable.Empty<string>();

Con eso tenemos una enumeración de string lista para ser usada con algún otro operador.

Page 93: Operadores de Linq

93 Operadores de LINQ

12.3 Range

El operador Range genera una secuencia de números enteros.

12.3.1 Range

public static IEnumerable<int> Range( int start, int count )

Al operador le pasamos dos números. El de inicio y el que indica la cantidad de números a generar. Por ejemplo si queremos generar 5 números haríamos:

IEnumerable<int> numeros = Enumerable.Range(1, 5);

Le estamos diciendo que empiece por 1 y que genere cinco números, entonces la salida sería:

1 2 3 4 5

Hay que fijarse que genera los números en orden, si quisiéramos por ejemplo los siguientes 5 números pares empezando por el 2 necesitaríamos la ayuda de otro operador, por ejemplo:

IEnumerable<int> numeros = Enumerable.Range(2, 5) .Select(x => x * 2);

Cada número lo multiplicamos por 2 y ya está :P

4 6 8 10 12

Page 94: Operadores de Linq

94 12. Generation (Generación)

12.4 Repeat

El operador Repeat genera una secuencia de un elemento repetido x veces.

12.4.1 Repeat

public static IEnumerable<TResult> Repeat<TResult>( TResult element, int count )

Al operador le indicamos el elemento a repetir y la cantidad de veces. Por ejemplo, vamos a crear una enumeración que contenta la cadena El Blog de Fox 5 veces:

IEnumerable<string> blogFox = Enumerable.Repeat("El Blog de Fox", 5);

O sea:

El Blog de Fox El Blog de Fox El Blog de Fox El Blog de Fox El Blog de Fox

Page 95: Operadores de Linq

95 Operadores de LINQ

13. Quantifier (Cuantificadores)

13.1 All

El operador All comprueba si todos los elementos de una secuencia cumplen una condición.

13.1.1 Código necesario para los ejemplos

Una lista de nombres:

List<string> nombres = new List<string> { "Jesus", "Maria", "Julia", "Carla" };

13.1.2 All

public static bool All<TSource>( this IEnumerable<TSource> source, Func<TSource, bool> predicate )

Como parámetro recibe un delegado que será la condición que ha de cumplir (o no) los elementos de una secuencia. Devuelve un bool. Por ejemplo, ¿Todos los nombres de la lista tienen 5 caracteres?:

bool cinco = nombres.All(n => n.Length == 5);

¿Verdad que si? Claro que si:

True ¿Pero terminan todos los nombres por a

bool a = nombres.All(n => n.EndsWith("a"));

Casi, así que:

False

Page 96: Operadores de Linq

96 13. Quantifier (Cuantificadores)

13.2 Any

El operador Any comprueba si una secuencia contiene elementos ya sean de cualquier tipo o con una condición.

13.2.1 Código necesario para los ejemplos

Una lista de nombres:

List<string> nombres = new List<string> { "Jesus", "Maria", "Julia", "Carla" };

Una lista vacía:

List<int> listaVacia = new List<int>();

13.2.2 Any estándar

public static bool Any<TSource>( this IEnumerable<TSource> source )

El operador se usa en una secuencia para determinar si contiene elementos o no. Devuelve un bool. Por ejemplo, con la lista de nombres:

bool any = nombres.Any();

Sí que hay nombres:

True

Pero hay algo en la lista de números:

bool any = listaVacia.Any();

No, no hay nada:

False

Page 97: Operadores de Linq

97 Operadores de LINQ

13.2.3 Any + condición

public static bool Any<TSource>( this IEnumerable<TSource> source, Func<TSource, bool> predicate )

En esta sobrecarga comprobaremos si existe algún elemento que cumpla la condición que pasaremos como parámetro al operador. Solo hace falta que uno cumpla la condición. ¿Algún nombre que termine en s?:

bool any = nombres.Any(n => n.EndsWith("s"));

Sí que hay sí:

True

¿Alguno de 6 caracteres?:

bool any = nombres.Any(n => n.Length == 6);

No, todos de 5:

False

13.3 Contains

El operador Contains comprueba si una secuencia contiene un elemento especificado.

13.3.1 Código necesario para los ejemplos

Una lista de nombres:

List<string> nombres = new List<string> { "Jesus", "Maria", "Julia", "Carla" };

Page 98: Operadores de Linq

98 13. Quantifier (Cuantificadores)

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(); } }

13.3.2 Contains estándar

public static bool Contains<TSource>( this IEnumerable<TSource> source, TSource value )

Le pasamos como parámetro el valor que queremos ver si existe en la secuencia. Devuelve un bool. ¿Habrá alguien con el nombre Julia en nuestra lista de nombres?:

bool julia = nombres.Contains("Julia");

Sí, sin duda:

True

¿Y algún Álvaro?:

bool alvaro = nombres.Contains("Alvaro");

No, esta vez no:

False

Page 99: Operadores de Linq

99 Operadores de LINQ

13.3.3 Contains + comparador personalizado

public static bool Contains<TSource>( this IEnumerable<TSource> source, TSource value, IEqualityComparer<TSource> comparer )

Ahora tenemos la posibilidad de usar nuestro propio comparador personalizado. ¿Algún nombre que empiece por C?:

bool c = nombres.Contains("C", new ComparadorInicial());

Sí, está Carla:

True

¿Alguno que empiece por H?:

bool h = nombres.Contains("H", new ComparadorInicial());

No.

False

Page 100: Operadores de Linq

100 14. Aggregation (Agregación)

14. Aggregation (Agregación)

14.1 Aggregate

El operador Aggregate va acumulando los elementos de una secuencia aplicando una función.

14.1.1 Código necesario para los ejemplos

Una lista de números:

List<int> numeros = new List<int> { 5, 2, 20, 5, 4, 4 };

14.1.2 Aggregate estándar

public static TSource Aggregate<TSource>( this IEnumerable<TSource> source, Func<TSource, TSource, TSource> func )

Este operador es bien complicado de entender al principio, voy a poner el ejemplo y explicarlo con él:

int suma = numeros.Aggregate((an, pos) => an + pos);

Como veis, el operador toma como parámetro un delegado el cual recibe 2 valores y devuelve otro. El primer elemento que recibe es el resultado de dicho delegado en la iteración anterior y el segundo es el siguiente elemento. En la primera iteración al no haber ningún resultado todavía, el primer valor es el primer elemento y el segundo valor es el segundo elemento. Como esto es difícil de asimilar, vamos a hacer una especie de "debugging": Primera iteración: an -> 5 pos -> 2 Segunda iteración: an -> 7 pos -> 20

Page 101: Operadores de Linq

101 Operadores de LINQ

Tercera iteración: an -> 27 pos -> 5 Y así hasta terminar con todos los elementos. Como se puede ver, empieza tomando los 2 primeros valores, los suma (que es lo que hace nuestro delegado) y devuelve la suma. Esa suma será el primer valor del delegado y el segundo será un nuevo elemento de la secuencia. Los volverá a sumar y así hasta que termina. En otras palabras, va acumulando los resultados hasta llegar al final. El resultado final es:

40

14.1.3 Aggregate + valor de inicio

public static TAccumulate Aggregate<TSource, TAccumulate>( this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func )

Esta sobrecarga recibe un valor de inicio. Como ya sabes, el operador Aggregate va iterando por cada elemento y acumulando los resultados. Al principio no tiene nada acumulado, pero con esta sobrecarga podemos especificar un valor de inicio. Por ejemplo, queremos a volver a sumar los números, pero ahora en vez de empezar con el acumulador a 0, lo iniciamos a 20:

int suma = numeros.Aggregate(20, (an, pos) => an + pos);

Entonces en la primera iteración sería: an -> 20 pos -> 5 Entonces el resultado sería:

60

Page 102: Operadores de Linq

102 14. Aggregation (Agregación)

14.1.4 Aggregate + valor de inicio + funcion elemento resultante

public static TResult Aggregate<TSource, TAccumulate, TResult>( this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func, Func<TAccumulate, TResult> resultSelector )

Además del valor de inicio, ahora tenemos la posibilidad de aplicarle una función al resultado final, o sea, al valor acumulado que hemos conseguido al terminar todas las iteraciones. Por ejemplo, vamos a volver a sumar los números, empezaremos por 20 y cuando terminemos lo vamos a dividir por 2:

int suma = numeros.Aggregate(20, (an, pos) => an + pos, res => res / 2);

¿Ya no nos cuesta entenderlo verdad? Empezamos a sumar con el contador a 20, y terminamos con el valor 60. Ese valor lo dividimos entre 2 así que el resultado es:

30

14.2 Average

Con el operador Average podemos calcular la media de una secuencia de valores numéricos.

14.2.1 Código necesario para los ejemplos

Una lista de números enteros:

ist<int> numeros = new List<int> { 5, 2, 20, 5, 4, 4 };

Una lista de números en coma flotante con un valor nulo:

List<double?> numeros2 = new List<double?> { null, 42, 5.4, 3.2, 2, 4 };

Page 103: Operadores de Linq

103 Operadores de LINQ

14.2.2 Average estándar

public static TResult Average<TResult>( this IEnumerable<TResult> source )

El operador Average tiene múltiples sobrecargas, pero todas hacen lo mismo solo que cada una trabaja con un tipo distinto. La lista de tipos con la que trabaja average es:

Decimal Decimal? Double Double? Int32 Int32? Int64 Int64? Single Single?

¿Cuál es la media de la primera secuencia de números?:

double media = numeros.Average();

El resultado es:

6,67

¿Y cuál es la media de la segunda secuencia de números que además tiene un valor nulo?:

double? media = numeros2.Average();

Es:

11,32

Page 104: Operadores de Linq

104 14. Aggregation (Agregación)

14.2.3 Average + función de transformación

En esta sobrecarga podemos aplicar una función de transformación a cada elemento antes de hacer la media. Vamos a multiplicar por dos cada número de la secuencia de enteros y hacer la media luego:

double media = numeros.Average(n => n * 2);

El resultado es:

13,33

14.3 Count

El operador Count cuenta el número de elementos de una secuencia.

14.3.1 Código necesario para los ejemplos

Una lista de nombres:

List<string> nombres = new List<string> { "Jesus", "Maria", "Julia", "Alvaro", "Manolo" };

14.3.2 Count estándar

public static int Count<TSource>( this IEnumerable<TSource> source )

Usamos Count en una secuencia en la que queremos contar sus elementos. Devuelve un int con el número de elementos de dicha secuencia. Por ejemplo, ¿Cuántos nombres tenemos?

int count = nombres.Count();

La respuesta es:

5

Page 105: Operadores de Linq

105 Operadores de LINQ

14.3.3 Count + condición

public static int Count<TSource>( this IEnumerable<TSource> source, Func<TSource, bool> predicate )

Si lo que realmente queremos es contar la cantidad de elementos de una secuencia que cumplen cierta condición, podemos ahora usar un delegado que usaremos como dicha condición. ¿Cuántos nombres terminan en o?

int count = nombres.Count(n => n.EndsWith("o"));

La respuesta es:

2

14.4 LongCount

El operador LongCount cuenta el número de elementos de una secuencia. A diferencia de Count, LongCount devuelve un long y no un int.

14.4.1 Código necesario para los ejemplos

Una lista de nombres:

List<string> nombres = new List<string> { "Jesus", "Maria", Julia", "Alvaro", "Manolo" };

14.4.2 LongCount estándar

public static long LongCount<TSource>( this IEnumerable<TSource> source )

Usamos LongCount en una secuencia en la que queremos contar sus elementos. Devuelve un long con el número de elementos de dicha secuencia. Por ejemplo, ¿Cuántos nombres tenemos?

long count = nombres.LongCount();

Page 106: Operadores de Linq

106 14. Aggregation (Agregación)

La respuesta es:

5

14.4.3 LongCount + condición

public static long LongCount<TSource>( this IEnumerable<TSource> source, Func<TSource, bool> predicate )

Si lo que realmente queremos es contar la cantidad de elementos de una secuencia que cumplen cierta condición, podemos ahora usar un delegado que usaremos como dicha condición. ¿Cuántos nombres empiezan por J?

long count = nombres.LongCount(n => n.StartsWith("J"));

La respuesta es:

2

14.5 Max

El operador Max devuelve el valor máximo de una secuencia de valores.

14.5.1 Código necesario para los ejemplos

Una lista de números enteros:

List<int> numeros = new List<int> { 5, 2, 20, 5, 4, 4 };

14.5.2 Max estándar

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

El operador Max tiene múltiples sobrecargas, pero todas hacen lo mismo solo que cada una trabaja con un tipo distinto. La lista de tipos con la que trabaja Max es:

Decimal Decimal? Double

Page 107: Operadores de Linq

107 Operadores de LINQ

Double? Int32 Int32? Int64 Int64? Single Single?

¿Cuál es el valor máximo de nuestra lista de números?:

int maximo = numeros.Max();

Usamos la sobrecarga correspondiente para el tipo de datos correspondiente de la secuencia. El resultado es:

20

14.5.3 Max + función de transformación

public static TResult Max<TSource, TResult>( this IEnumerable<TSource> source, Func<TSource, TResult> selector )

Ésta sobrecarga aplicará una función a cada elemento antes de buscar el valor máximo de la secuencia. Vamos a calcular el cuadrado de cada número y luego ver el mayor:

int maximo = numeros.Max(n => n * n);

El resultado es:

400

Page 108: Operadores de Linq

108 14. Aggregation (Agregación)

14.6 Min

El operador Min devuelve el valor mínimo de una secuencia de valores.

14.6.1 Código necesario para los ejemplos

Una lista de números enteros:

List<int> numeros = new List<int> { 5, 2, 20, 5, 4, 4 };

14.6.2 Min estándar

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

El operador Min tiene múltiples sobrecargas, pero todas hacen lo mismo solo que cada una trabaja con un tipo distinto. La lista de tipos con la que trabaja Min es:

Decimal Decimal? Double Double? Int32 Int32? Int64 Int64? Single Single?

¿Cuál es el valor mínimo de nuestra lista de números?:

int minimo = numeros.Min();

Usamos la sobrecarga correspondiente para el tipo de datos correspondiente de la secuencia. El resultado es:

2

Page 109: Operadores de Linq

109 Operadores de LINQ

14.6.3 Min + función de transformación

public static TResult Min<TSource, TResult>( this IEnumerable<TSource> source, Func<TSource, TResult> selector )

Ésta sobrecarga aplicará una función a cada elemento antes de buscar el valor mínimo de la secuencia. Vamos a multiplicar cada número por cinco y calcular el mínimo:

int minimo = numeros.Min(n => n * 5);

El resultado es:

10

14.7 Sum

El operador Sum devuelve la suma de los valores de una secuencia.

14.7.1 Código necesario para los ejemplos

Una lista de números enteros:

List<int> numeros = new List<int> { 5, 2, 20, 5, 4, 4 };

14.7.2 Sum estándar

El operador Sum tiene múltiples sobrecargas, pero todas hacen lo mismo solo que cada una trabaja con un tipo distinto. La lista de tipos con la que trabaja Sum es:

Decimal Decimal? Double Double? Int32 Int32? Int64 Int64? Single Single?

Page 110: Operadores de Linq

110 14. Aggregation (Agregación)

¿Cuál es la suma de los números de la secuencia?

int suma = numeros.Sum();

¿Recuerdas que hicimos esto mismo usando Average? Obviamente esta es la forma correcta de hacer este tipo de operación. El resultado es:

40

14.7.3 Sum + función de transformación

Ésta sobrecarga aplicará una función a cada elemento antes de buscar el valor mínimo de la secuencia. Vamos a restarle tres a cada número y luego hacer la suma:

int suma = numeros.Sum(n => n - 3);

El resultado es:

22

Page 111: Operadores de Linq