operadores de linq jesus rodrigues rodrigues

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

Upload: rodrigo-saraya-salas

Post on 22-Nov-2015

31 views

Category:

Documents


2 download

TRANSCRIPT

  • Operadores de LINQ

    Jess Rodrguez Rodrguez

  • 2 Operadores de LINQ

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

  • 3 Operadores de LINQ

    OPERADORES DE LINQ 1

    1. FILTERING (FILTRADO) 6

    1.1 OFTYPE 61.2 WHERE 7

    2. PROJECTION (PROYECCIN) 9

    2.1 SELECT 9

    3. PARTITIONING (PARTICIONADO) 17

    3.1 SKIP 173.2 SKIPWHILE 183.4 TAKEWHILE 21

    4. JOIN (UNIN) 23

    4.1 GROUPJOIN 23

    5. CONCATENATION (CONCATENACIN) 32

    5.1 CONCAT 32

    6. ORDERING (ORDENACIN) 33

    6.1 ORDERBY 336.2 ORDERBYDESCENDING 356.3 REVERSE 386.4 THENBY 396.5 THENBYDESCENDING 42

    7. GROUPING (AGRUPACIN) 45

  • 4 Operadores de LINQ

    7.1 GROUPBY 457.2 TOLOOKUP 55

    8. SET (CONJUNTO) 60

    8.1 DISTINCT 60

    8. SET (CONJUNTO) 62

    8.3 INTERSECT 648.4 UNION 67

    9. CONVERSION (CONVERSIN) 70

    9.1 ASENUMERABLE 709.2 ASQUERYABLE 719.3 CAST 729.4 TOARRAY 739.5 TODICTIONARY 749.6 TOLIST 78

    10. EQUALITY (IGUALDAD) 80

    10.1 SEQUENCEEQUAL 80

    11. ELEMENT (ELEMENTO) 82

    11.1 ELEMENTAT 8211.2 ELEMENTATORDEFAULT 8211.3 FIRST 8311.4 FIRSTORDEFAULT 8411.5 LAST 8511.6 LASTORDEFAULT 8711.7 SINGLE 8811.8 SINGLEORDEFAULT 89

  • 5 Operadores de LINQ

    12. GENERATION (GENERACIN) 91

    12.1 DEFAULTIFEMPTY 9112.2 EMPTY 9212.3 RANGE 9312.4 REPEAT 94

    13. QUANTIFIER (CUANTIFICADORES) 95

    13.1 ALL 9513.2 ANY 9613.3 CONTAINS 97

    14. AGGREGATION (AGREGACIN) 100

    14.1 AGGREGATE 10014.2 AVERAGE 10214.3 COUNT 10414.4 LONGCOUNT 10514.5 MAX 10614.6 MIN 10814.7 SUM 109

  • 6 1. Filtering (Filtrado)

    1. Filtering (Filtrado)

    1.1 OfType

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

    1.1.1 Cdigo 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 OfType( this IEnumerable source )

    Le especificamos el tipo que queremos filtrar y nos devuelve una enumeracin de dicho tipo.

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

    IEnumerable cadenas = objetos.OfType();

    Si imprimimos el resultado, la salida sera:

    LINQ

    Perro

    Casa

  • 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 ms flexible puesto que admite como parmetro un delegado.

    1.2.1 Cdigo necesario para los ejemplos

    Lista de ttulos (imaginarios) de libros:

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

    1.2.2 Where estndar

    public static IEnumerable Where( this IEnumerable source, Func predicate )

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

    Queremos una nueva lista de libros donde slo estn los que sean de LINQ, para ello usamos Where.

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

    Adems, el operador Where se puede convertir a la sintaxis de un query expression:

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

  • 8 1. Filtering (Filtrado)

    En ambos casos, el resultado de imprimirlo sera:

    LINQ para torpes

    Empezando con LINQ

    LINQ Avanzado

    1.2.3 Where + ndice

    public static IEnumerable Where( this IEnumerable source, Func predicate )

    En este caso, el delegado acepta tambin un int el cual representa la posicin de cada elemento en la secuencia que estamos filtrando.

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

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

    Esto imprimira:

    LINQ para Torpes

    La versin de Where que acepta tambin el ndice, no puede ser convertida a la sintaxis de los query expressions.

  • 9 Operadores de LINQ

    2. Projection (Proyeccin)

    2.1 Select

    El operador Select nos permite proyectar cada elemento de una secuencia a una nueva enumeracin.

    2.1.1 Cdigo necesario para los ejemplos

    Una pequea clase representando una persona con una lista de gadgets:

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

    Y una lista de dichas personas:

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

    "Movil Ladrillo"} ) };

  • 10 2. Projection (Proyeccin)

    2.1.2 Select estndar

    public static IEnumerable SelectMany( this IEnumerable source, Func selector )

    El argumento es un delegado que acepta un elemento de la secuencia y devuelve el elemento que queramos proyectar. El operador devuelve una enumeracin 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 enumeracin:

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

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

    IEnumerable nombres = from persona in personas select persona.Nombre;

    En ambos casos el resultado de imprimir la secuencia sera:

    Juan

    Alvaro

    Adems podemos crear tipos annimos 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 creara un tipo annimo y dentro sera algo as:

    Nombre=Jesus Edad=24

    Nombre=Juan Edad=20

    Nombre=Alvaro Edad=25

  • 11 Operadores de LINQ

    Por otro lado, si quisiramos imprimir la lista de gadgets, haramos algo as:

    ListasGadgets = from persona in personas select persona.Gadgets;

    Esa proyeccin devuelve una enumeracin de listas, as que tendramos 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 versin mejorada de este ltimo ejemplo usando SelectMany.

    2.1.3 Select + ndice

    public static IEnumerable Select( this IEnumerable source, Func selector )

    El delegado recibe el elemento en cuestin, adems del ndice de la posicin del elemento en la secuencia.

    Vamos a suponer que queremos crear un tipo annimo con cada ndice y nombre:

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

  • 12 2. Projection (Proyeccin)

    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 excepcin de que SelectMany coge cada elemento que proyecta, lo convierte en una enumeracin, y luego concatena todas las enumeraciones.

    2.2.1 Cdigo 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 Gadgets { get; set; } public Persona(string nombre, int edad, List gadgets) { Nombre = nombre; Edad = edad; Gadgets = gadgets; } }

    Y una lista de dichas personas:

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

  • 13 Operadores de LINQ

    new Persona("Alvaro", 24, new List {"Pentium",

    "Movil Ladrillo"} ) };

    2.2.2 SelectMany estndar

    public static IEnumerable SelectMany( this IEnumerable source, Func selector )

    El argumento que toma es un delegado el cual recibe el elemento y devuelve una enumeracin con la proyeccin de cada elemento como una enumeracin. Finalmente devuelve todo en una enumeracin.

    Como este operador es difcil 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 hara as:

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

    El problema es que esto es una enumeracin 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 ms simple:

    IEnumerable 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 enumeracin con los elementos de cada lista de gadgets. Justo lo que estbamos buscando.

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

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

  • 14 2. Projection (Proyeccin)

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

    Por supuesto es ms cmodo usar SelectMany.

    2.2.3 SelectMany + ndice

    public static IEnumerable SelectMany( this IEnumerable source, Func selector )

    El delegado, adems del elemento, toma un int el cual es el ndice del elemento. Devuelve una enumeracin con la proyeccin de cada elemento como una enumeracin.

    Queremos una lista de gadgets que adems 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 podis ver, "Ipod" y "HTC Diamond" pertenecen al primer elemento, as que el ndice es 0.

    2.2.4 SelectMany + Funcin

    public static IEnumerable SelectMany( this IEnumerable source, Func collectionSelector, Func resultSelector )

  • 15 Operadores de LINQ

    En sta sobrecarga, recibimos tambin 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 maysculas, usaramos algo as:

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

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

    Como se puede ver, el segundo parmetro que le estamos pasando es una lambda que recibe dos parmetros: el objeto persona que es el elemento de la secuencia y cada elemento que estamos proyectando; luego simplemente le aplicamos el mtodo ToUpper a cada gadget y ya est.

    El resultado es:

    IPOD

    HTC DIAMOND

    VAIO

    ANDROID

    PENTIUM

    MOVIL LADRILLO

    2.2.5 SelectMany + Funcin + ndice

    public static IEnumerable SelectMany( this IEnumerable source, Func CollectionSelector, Func resultSelector )

    Esta sobrecarga es una combinacin entre las dos anteriores, tiene la funcin 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.

  • 16 2. Projection (Proyeccin)

    Queremos imprimir los gadgets en plan:

    NUMERO GADGET

    Para ello usaremos esto:

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

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

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

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

    El resultado es el que esperamos:

    0 - IPOD

    0 - HTC DIAMOND

    1 - VAIO

    1 - ANDROID

    2 - PENTIUM

    2 - MOVIL LADRILLO

  • 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 Cdigo necesario para los ejemplos

    Una lista de nombres:

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

    3.1.2 Skip

    public static IEnumerable Skip( this IEnumerable source, int count )

    Skip toma como argumento un nmero que ser la cantidad de elementos de la secuencia a ignorar. Devuelve una enumeracin con los elementos restantes.

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

    IEnumerable mujeres = nombres.Skip(5);

  • 18 3. Partitioning (Particionado)

    El resultado sera:

    Rosi

    Maria

    Amanda

    Julia

    Si como argumento le pasamos un cero o un nmero negativo, no ignorar nada.

    3.2 SkipWhile

    El operador SkipWhile como su homnimo Skip sirve para ignorar elementos. Pero dada una condicin y no un nmero.

    3.2.1 Cdigo necesario para los ejemplos

    Una lista de calificaciones:

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

    3.2.2 SkipWhile estndar

    public static IEnumerable SkipWhile( this IEnumerable source, Func predicate )

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

    Cada elemento ir pasando por el delegado y devolver true o false dependiendo de una condicin. Mientras vaya devolviendo true, esos elementos se irn 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 ordenacin, pero puesto que prefiero mostrar ejemplos que involucren la menor cantidad

  • 19 Operadores de LINQ

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

    Imaginemos que nuestra condicin es ir ignorando toda calificacin considerada suspendida hasta que encontremos una aprobada:

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

    Aqu la condicin 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 enumeracin todas las notas restantes estn aprobadas o no.

    El resultado es:

    6

    6

    7.6

    9

    10

    3.2.3 SkipWhile + ndice

    public static IEnumerable SkipWhile( this IEnumerable source, Func predicate )

    En esta sobrecarga, el delegado adems del elemento, recibe tambin el ndice de dicho elemento en la secuencia. Lo cual nos da ms posibilidades a la hora de ignorar elementos.

    Como antes, es difcil mostrar el uso de esta sobrecarga si no es en conjuncin con otros operadores, pero como la sencillez es lo ms importante a la hora de aprender, vamos con este ejemplo:

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

    Estamos ignorando todas las notas hasta que una de ellas sea ms alta que su ndice + 0.5.

  • 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 Cdigo necesario para los ejemplos

    Una lista de nombres:

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

    3.3.2 Take

    public static IEnumerable Take( this IEnumerable source, int count )

    Take toma como argumento un nmero que ser la cantidad de elementos de la secuencia a devolver. Devuelve una enumeracin con dichos elementos ignorando el resto.

    En este caso, si necesitamos los 5 primeros nombres, haramos algo as:

    IEnumerable hombres = nombres.Take(5);

  • 21 Operadores de LINQ

    Esto imprimira:

    Jesus

    Alvaro

    Manolo

    Juan

    Paco

    Si le pasamos un nmero negativo o cero, no devolver nada.

    3.4 TakeWhile

    El operador TakeWhile como su homnimo Take sirve para devolver los elementos de una secuencia pero en este caso los ir devolviendo mientras la condicin devuelva true.

    3.4.1 Cdigo necesario para los ejemplos

    Una lista de calificaciones:

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

    3.4.2 TakeWhile estndar

    public static IEnumerable TakeWhile( this IEnumerable source, Func predicate )

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

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

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

  • 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 regaar a aquellos que no aprobaron :P):

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

    Mientras la nota sea menor a 5, ir devolvindola. 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 TakeWhile( this IEnumerable source, Func predicate )

    En esta sobrecarga, el delegado adems del elemento, recibe tambin el ndice de dicho elemento en la secuencia. Lo cual nos da ms posibilidades a la hora de devolver elementos.

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

    Mientras la nota sea menor al valor del ndice + 0.5, aadirla a la enumeracin.

  • 23 Operadores de LINQ

    4. Join (Unin)

    4.1 GroupJoin

    El operador GroupJoin nos permite establecer una relacin entre 2 secuencias y agrupar los resultados. Bsicamente agrupa un elemento de la secuencia externa a una coleccin de elementos de la secuencia interna.

    4.1.1 GroupJoin estndar

    4.1.1.1 Cdigo 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("Cdiz"); Provincia malaga = new Provincia("Mlaga"); Provincia madrid = new Provincia("Madrid"); Provincia barcelona = new Provincia("Barcelona"); Ciudad algeciras = new Ciudad("Algeciras", cadiz); Ciudad jerez = new Ciudad("Jerez", cadiz);

  • 24 4. Join (Unin)

    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 listaProvincias = new List { cadiz, malaga, madrid, barcelona }; List listaCiudades = new List { algeciras, jerez, ronda, churriana, alcobendas, fuenlabrada };

    4.1.1.2 Definicin

    public static IEnumerable GroupJoin( this IEnumerable outer, IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func resultSelector )

    El operador recibe varios parmetros:

    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 enumeracin que saldr como resultado al combinar un elemento de la secuencia externa con una coleccin de elementos de la secuencia interna.

    El operador devuelve la enumeracin resultante.

    Vamos a verlo con un ejemplo. Queremos crear una enumeracin donde agruparemos las provincias con una coleccin de ciudades que estn dentro de dicha provincia:

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

  • 25 Operadores de LINQ

    Por pasos:

    El primer parmetro 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 annimo que consistir en una provincia ms una lista de ciudades.

    Parece complicado pero no lo es, simplemente colocamos como clave los 2 elementos que harn la unin, 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 Localizacin en comn. Llamar a ese grupo ciudades. De ah creamos un tipo annimo con el nombre de la clave (es el elemento que hemos usado para hacer la unin, 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 podra ser as:

    Cdiz - (Algeciras, Jerez)

    Mlaga - (Ronda, Churriana)

    Madrid - (Alcobendas, Fuenlabrada)

    Barcelona - ()

  • 26 4. Join (Unin)

    4.1.2 GroupJoin + Comparador personalizado

    4.1.2.1 Cdigo necesario

    Una lista de iniciales y otra de palabras:

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

    List listaPalabras = new List { "Arroz", "Antena",

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

    Un comparador personalizado:

    public class ComparadorIniciales : IEqualityComparer { 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 Definicin

    public static IEnumerable GroupJoin( this IEnumerable outer, IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func resultSelector, IEqualityComparer comparer)

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

    En el anterior ejemplo, habamos 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 unin donde cada inicial estar asociada a una coleccin de palabras que empiecen por dicha inicial.

  • 27 Operadores de LINQ

    El cdigo sera:

    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 estndar, simplemente aadimos 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 y dicha interfaz viene con 2 mtodos.

    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 jams de los jamases usaramos esta sobrecarga para hacer un trabajo como este, puesto que el operador estndar es capaz de hacer una unin de este tipo o directamente usar otro operador como SelectMany. Simplemente esto es un ejemplo para que veis como implementar un comparador personalizado y usarlo.

    Finalmente el resultado es algo tipo:

    A - (Arroz, Antena)

    B - (Barco)

    C - (Caballo)

    D - (Decir)

  • 28 4. Join (Unin)

    4.2 Join

    El operador Join sirve para establecer una relacin entre 2 secuencias comparando sus claves.

    4.2.1 Join estndar

    4.2.1.1 Cdigo 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("Cdiz"); Provincia malaga = new Provincia("Mlaga"); 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);

  • 29 Operadores de LINQ

    Y las metemos en listas:

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

    4.2.1.2 Definicin

    public static IEnumerable Join( this IEnumerable outer, IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func resultSelector )

    El operador recibe varios parmetros:

    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 enumeracin que saldr como resultado al combinar un elemento de la secuencia externa con un elemento de la secuencia interna.

    El operador devuelve la enumeracin 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 relacin. Eso quiere decir, que si tenemos 2 ciudades de Cdiz, saldrn 2 entradas en vez de 1 con varios elementos.

  • 30 4. Join (Unin)

    Esto es lo que comnmente se llama un Inner Join que bsicamente es encontrar la relacin entre 2 secuencias, y cada elemento de las 2 secuencias que se relacionen irn a parar a una nueva enumeracin.

    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 extrao parecido a un Inner Join en SQL Verdad? :)

    En ambos casos el resultado podra ser del tipo:

    Cdiz - Algeciras

    Cdiz - Jerez

    Mlaga - Ronda

    Mlaga - Churriana

    Madrid - Alcobendas

    Madrid Fuenlabrada

    4.2.2 Join + Comparador personalizado

    4.2.2.1 Cdigo necesario

    Una lista de iniciales y otra de palabras:

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

    List listaPalabras = new List { "Arroz", "Antena",

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

    Un comparador personalizado:

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

  • 31 Operadores de LINQ

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

    4.2.2.2 Definicin

    public static IEnumerable Join( this IEnumerable outer, IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func resultSelector, IEqualityComparer comparer )

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

    En el anterior ejemplo, habamos 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 unin 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 cmo crear y usar un comparador revisa la definicin del operador GroupJoin.

    El resultado es:

    A - Arroz

    A - Antena

    B - Barco

    C - Caballo

    D Decir

  • 32 5. Concatenation (Concatenacin)

    5. Concatenation (Concatenacin)

    5.1 Concat

    El operador Concat sirve para concatenar 2 secuencias.

    5.1.1 Cdigo necesario para los ejemplos

    Un par de listas de nombres:

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

    5.1.2 Concat

    public static IEnumerable Concat( this IEnumerable first, IEnumerable second )

    Concat recibe como parmetro una secuencia que queremos concatenar a la secuencia que est ejecutando el operador. Devuelve una sola enumeracin que contiene las dos enumeraciones usadas.

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

    IEnumerable nombres = nombres1.Concat(nombres2);

    El resultado sera:

    Jesus

    Alvaro

    Manolo

    Rosi

    Amanda

    Julia

  • 33 Operadores de LINQ

    6. Ordering (Ordenacin)

    6.1 OrderBy

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

    6.1.1 Cdigo 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 personas = new List { new Persona("Rodriguez, Jess", 24), new Persona("Perez, Juan", 15), new Persona("Garca, Javier", 45), new Persona("Toledo, Mara", 37) };

    Un comparador personalizado:

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

  • 34 6. Ordering (Ordenacin)

    6.1.2 OrderBy estndar

    public static IOrderedEnumerable OrderBy( this IEnumerable source, Func keySelector )

    OrderBy toma como parmetro la clave que se usar para ordenar la secuencia. Devuelve una enumeracin ordenada.

    Por ejemplo, si queremos ordenar la lista de personas por el nombre, usaramos este cdigo:

    IEnumerable 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 ascendente = from persona in personas orderby persona.Nombre select persona;

    El resultado sera:

    Nombre=Garca, Javier Edad=45

    Nombre=Perez, Juan Edad=15

    Nombre=Rodriguez, Jess Edad=24

    Nombre=Toledo, Mara Edad=37

    6.1.3 OrderBy + Comparador personalizado:

    public static IOrderedEnumerable OrderBy( this IEnumerable source, Func keySelector, IComparer comparer )

    Aqu adems de la clave que usaremos para ordenar, usaremos un comparador personalizado.

  • 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 cdigo que necesitamos para trabajar con este operador, con ello escribimos:

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

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

    El resultado es:

    Nombre=Garca, Javier Edad=45

    Nombre=Rodriguez, Jess Edad=24

    Nombre=Perez, Juan Edad=15

    Nombre=Toledo, Mara Edad=37

    6.2 OrderByDescending

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

    6.2.1 Cdigo 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; } }

  • 36 6. Ordering (Ordenacin)

    Una lista de personas:

    List personas = new List { new Persona("Rodriguez, Jess", 24), new Persona("Perez, Juan", 15), new Persona("Garca, Javier", 45), new Persona("Toledo, Mara", 37) };

    Un comparador personalizado:

    public class OrdenaNombre : IComparer { 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 estndar

    public static IOrderedEnumerable OrderByDescending( this IEnumerable source, Func keySelector )

    OrderByDescending toma como parmetro la clave que se usar para ordenar la secuencia (de forma descendente). Devuelve una enumeracin ordenada.

    Por ejemplo, si queremos ordenar la lista de personas por el nombre (de forma descendente), usaramos este cdigo:

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

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

  • 37 Operadores de LINQ

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

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

    El resultado sera:

    Nombre=Toledo, Mara Edad=37

    Nombre=Rodriguez, Jess Edad=24

    Nombre=Perez, Juan Edad=15

    Nombre=Garca, Javier Edad=45

    6.2.3 OrderByDescending + Comparador personalizado

    public static IOrderedEnumerable OrderByDescending( this IEnumerable source, Func keySelector, IComparer comparer )

    Aqu adems 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 cdigo que necesitamos para trabajar con este operador, con ello escribimos:

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

    new OrdenaNombre());

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

    El resultado es:

    Nombre=Toledo, Mara Edad=37

    Nombre=Perez, Juan Edad=15

    Nombre=Rodriguez, Jess Edad=24

    Nombre=Garca, Javier Edad=45

  • 38 6. Ordering (Ordenacin)

    6.3 Reverse

    El operador Reverse invierte una secuencia.

    6.3.1 Cdigo necesario para los ejemplos

    Una lista de cadenas representando nmeros:

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

    6.3.2 Reverse

    public static IEnumerable Reverse( this IEnumerable source )

    Este operador no toma ningn parmetro cuando lo usas en una secuencia (ya que el parmetro que empieza por this indica que es un mtodo de extensin).

    Cuando lo usas como un mtodo de extensin en una secuencia, modifica la misma secuencia en s pero no devuelve nada.

    Por ejemplo:

    numeros.Reverse();

    Eso le dara la vuelta a la lista de nmeros directamente.

    O sea:

    Cinco

    Cuatro

    Tres

    Dos

    Uno

  • 39 Operadores de LINQ

    6.4 ThenBy

    El operador ThenBy toma una enumeracin ordenada y le hace una ordenacin adicional usando otra clave.

    6.4.1 Cdigo 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 personas = new List { new Persona("Rodriguez, Jess", 24), new Persona("Rodriguez, Jess", 15), new Persona("Perez, Juan", 15), new Persona("Garca, Javier", 24), new Persona("Toledo, Mara", 37) };

    Un comparador personalizado:

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

  • 40 6. Ordering (Ordenacin)

    6.4.2 ThenBy estndar

    public static IOrderedEnumerable ThenBy( this IOrderedEnumerable source, Func keySelector )

    El operador ThenBy toma como argumento la clave que se usar para la ordenacin adicional. Devuelve la enumeracin ordenada.

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

    IEnumerable dobleOrdenacin = 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 enumeracin ordenada).

    ThenBy es un mtodo de extensin de dicho IOrderedEnumerable as que a eso le aplicamos ThenBy.

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

    Tambin podemos hacer esto usando una query expression:

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

    select persona;

    En ambos casos, el resultado es:

    Nombre=Garca, Javier Edad=24

    Nombre=Perez, Juan Edad=15

    Nombre=Rodriguez, Jess Edad=15

    Nombre=Rodriguez, Jess Edad=24

    Nombre=Toledo, Mara Edad=37

  • 41 Operadores de LINQ

    6.4.3 ThenBy + Comparador personalizado

    public static IOrderedEnumerable ThenBy( this IOrderedEnumerable source, Func keySelector, IComparer comparer )

    Aqu adems 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 cdigo:

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

    Como puedes ver, ThenBy trabaja con una enumeracin ordenada, pero le da igual si est ordenada ascendentemente o descendentemente.

    El resultado es:

    Nombre=Toledo, Mara Edad=37

    Nombre=Garca, Javier Edad=24

    Nombre=Rodriguez, Jess Edad=24

    Nombre=Rodriguez, Jess Edad=15

    Nombre=Perez, Juan Edad=15

  • 42 6. Ordering (Ordenacin)

    6.5 ThenByDescending

    El operador ThenByDescending toma una enumeracin ordenada y le hace una ordenacin adicional descendente usando otra clave.

    6.5.1 Cdigo 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 personas = new List { new Persona("Rodriguez, Jess", 24), new Persona("Rodriguez, Jess", 15), new Persona("Perez, Juan", 15), new Persona("Garca, Javier", 24), new Persona("Toledo, Mara", 37) };

    Un comparador personalizado:

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

  • 43 Operadores de LINQ

    6.5.2 ThenByDescending estndar

    public static IOrderedEnumerable ThenByDescending( this IOrderedEnumerable source, Func keySelector )

    El operador ThenByDescending toma como argumento la clave que se usar para la ordenacin adicional (que ser descendiente). Devuelve la enumeracin ordenada.

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

    IEnumerable 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 enumeracin ordenada).

    ThenByDescending es un mtodo de extensin de dicho IOrderedEnumerable as que a eso le aplicamos ThenByDescending.

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

    Tambin podemos hacer esto usando una query expression:

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

    select persona;

    En ambos casos, el resultado es:

    Nombre=Garca, Javier Edad=24

    Nombre=Perez, Juan Edad=15

    Nombre=Rodriguez, Jess Edad=24

    Nombre=Rodriguez, Jess Edad=15

    Nombre=Toledo, Mara Edad=37

  • 44 6. Ordering (Ordenacin)

    6.5.3 ThenByDescending + Comparador personalizado

    public static IOrderedEnumerable ThenByDescending( this IOrderedEnumerable source, Func keySelector, IComparer comparer )

    Aqu adems 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 cdigo:

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

    El resultado es:

    Nombre=Perez, Juan Edad=15

    Nombre=Rodriguez, Jess Edad=15

    Nombre=Rodriguez, Jess Edad=24

    Nombre=Garca, Javier Edad=24

    Nombre=Toledo, Mara Edad=37

  • 45 Operadores de LINQ

    7. Grouping (Agrupacin)

    7.1 GroupBy

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

    7.1.1 Cdigo 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 personas = new List { new Persona("Rodriguez, Jess", 24, "Cdiz"), new Persona("Bautista, Jess", 15, "Alicante"), new Persona("Perez, Juan", 15, "Cdiz"), new Persona("Garca, Javier", 24, "Mlaga"), new Persona("Toledo, Mara", 37, "Mlaga") };

    Un comparador personalizado:

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

  • 46 7. Grouping (Agrupacin)

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

    } public bool EsMayor(int edad) { return (edad >= 18); } } Un mtodo de ayuda:

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

    7.1.2 GroupBy estndar

    public static IEnumerable GroupBy( this IEnumerable source, Func keySelector )

    El operador GroupBy estndar simplemente recibe un delegado al que le pasamos la clave que usaremos para agrupar los elementos de la secuencia. Devuelve una enumeracin de elementos que tienen una clave comn.

    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 personasPorCiudad = personas.GroupBy(persona => persona.Ciudad);

    Nada difcil, le decimos que queremos agrupar la secuencia por la ciudad.

    Tambin podemos agrupar usando un query expression:

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

  • 47 Operadores de LINQ

    En ambos casos podramos 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:

    Cdiz

    Rodriguez, Jess (24)

    Perez, Juan (15)

    Alicante

    Bautista, Jess (15)

    Mlaga

    Garca, Javier (24)

    Toledo, Mara (37)

    7.1.3 GroupBy + comparador personalizado

    public static IEnumerable GroupBy( this IEnumerable source, Func keySelector, IEqualityComparer comparer )

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

  • 48 7. Grouping (Agrupacin)

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

    IEnumerable 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, Jess (Cdiz)

    24 - Garca, Javier (Mlaga)

    37 - Toledo, Mara (Mlaga)

    Menor

    15 - Bautista, Jess (Alicante)

    15 - Perez, Juan (Cdiz)

    7.1.4 GroupBy + proyector sencillo

    public static IEnumerable GroupBy( this IEnumerable source, Func keySelector, Func elementSelector )

    En esta sobrecarga podemos usar una funcin para proyectar los elementos de cada grupo.

  • 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 perCiudad = personas.GroupBy(p => p.Ciudad, p => p.Nombre);

    La diferencia con el operador estndar es que ya no estamos proyectando todo el objeto Persona, slo estamos proyectando el nombre.

    El resultado sera:

    Cdiz

    Rodriguez, Jess

    Perez, Juan

    Alicante

    Bautista, Jess

    Mlaga

    Garca, Javier

    Toledo, Mara

    7.1.5 GroupBy + proyector de enumeracin

    public static IEnumerable GroupBy( this IEnumerable source, Func keySelector, Func resultSelector )

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

  • 50 7. Grouping (Agrupacin)

    Como es algo complicado de entender (y an ms 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 annimo que contiene la clave y la cantidad de personas por cada ciudad.

    Ahora si volvemos a leer la descripcin de esta sobrecarga, nos queda ms claro. Estamos creando una enumeracin de tipos annimos compuestos por la ciudad y por la cantidad de personas de dicha ciudad.

    El resultado es:

    Ciudad=Cdiz NumPersonas=2

    Ciudad=Alicante NumPersonas=1

    Ciudad=Mlaga NumPersonas=2

    7.1.6 GroupBy + proyector sencillo + comparador personalizado

    public static IEnumerable GroupBy( this IEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer 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 agrupacin.

    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

  • 51 Operadores de LINQ

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

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

    El resultado sera algo as:

    Mayor

    Rodriguez, Jess

    Garca, Javier

    Toledo, Mara

    Menor

    Bautista, Jess

    Perez, Juan

    7.1.7 GroupBy + proyector sencillo + proyector de enumeracin

    public static IEnumerable GroupBy( this IEnumerable source, Func keySelector, Func elementSelector, Func 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 proyeccin a otro delegado el cual crea un elemento resultante. El operador devuelve una enumeracin de dicho elemento.

  • 52 7. Grouping (Agrupacin)

    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 enumeracin. La diferencia es que aqu en vez de pasarle una enumeracin de personas le he pasado una enumeracin de cadenas.

    La idea es que como no necesitamos toda la informacin 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=Cdiz NumPersonas=2

    Ciudad=Alicante NumPersonas=1

    Ciudad=Mlaga NumPersonas=2

    7.1.8 GroupBy + proyector de enumeracin + comparador personalizado

    public static IEnumerable GroupBy( this IEnumerable source, Func keySelector, Func resultSelector, IEqualityComparer comparer )

    Esta sobrecarga tiene el delegado de enumeracin que ya hemos visto en dos ocasiones y adems soporta un comparador personalizado.

  • 53 Operadores de LINQ

    Vamos a agrupar por mayora o minora 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 informacin 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 enumeracin + comparador personalizado

    public static IEnumerable GroupBy( this IEnumerable source, Func keySelector, Func elementSelector, Func resultSelector, IEqualityComparer comparer )

    He aqu la combinacin de todas las sobrecargas de este pequeo operador. Tiene su proyector de elementos, proyecta luego una enumeracin y todo con su comparador personalizado.

  • 54 7. Grouping (Agrupacin)

    Vamos a agrupar por mayora o minora 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 mnima y mxima del grupo y usamos el comparador personalizado para hacer las agrupaciones.

    El resultado es:

    Mayores de edad

    Edad mnima: 24

    Edad mxima: 37

    Menores de edad

    Edad mnima: 15

    Edad mxima: 15

  • 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 Cdigo 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 personas = new List { new Persona("Rodriguez, Jess", 24, "Cdiz"), new Persona("Bautista, Jess", 15, "Alicante"), new Persona("Perez, Juan", 15, "Cdiz"), new Persona("Garca, Javier", 24, "Mlaga"), new Persona("Toledo, Mara", 37, "Mlaga") };

    Un comparador personalizado:

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

  • 56 7. Grouping (Agrupacin)

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

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

    public static ILookup ToLookup( this IEnumerable source, Func keySelector )

    El operador ToLookup recibe un delegado que usaremos para seleccionar la clave. Este operador devuelve un objeto que implemente la interfaz ILookup.

    ILookup implementa a su vez IEnumerable. 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 ms claro vamos con un ejemplo:

    ILookup 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 enumeracin de personas (que vivan en esa ciudad).

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

    IEnumerable gaditanos = perCiudad["Cdiz"];

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

    Imprimir esa variable dara algo del tipo:

    Nombre=Rodriguez, Jess Edad=24 Ciudad=Cdiz

    Nombre=Perez, Juan Edad=15 Ciudad=Cdiz

  • 57 Operadores de LINQ

    7.2.3 ToLookup + comparador personalizado

    public static ILookup ToLookup( this IEnumerable source, Func keySelector, IEqualityComparer comparer )

    Esta sobrecarga toma adems un comparador personalizado para seleccionar la clave. Vemoslo con un ejemplo:

    ILookup 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 difcil de entender:

    IEnumerable mayores = mayorOMenor[20];

    No hay nadie con 20 aos, no? Es cierto, pero no es lo que le estamos pidiendo.

    Como sabris, la mayora de operadores de LINQ implementan la ejecucin diferida. Qu significa esto? Que cuando ejecutas el operador no ests realmente creando ninguna coleccin ni nada. Lo que obtienes del operador es un objeto que almacena la informacin necesaria para realizar la accin 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, tambin es mayor de edad, y como ambas lo son, devuelve la persona con dicha edad.

    En resumen, con esta sobrecarga, puedes pasarle cualquier nmero al objeto ILookup que si es mayor de 18 (mayora de edad) devolver todos los mayores de edad, y si es un nmero menor a 18, devolver aquellos que no lo son. Con la otra sobrecarga solo podas meterle valores especficos para que funcionase.

  • 58 7. Grouping (Agrupacin)

    As que si le pasamos 20 conseguiremos:

    Nombre=Rodriguez, Jess Edad=24 Ciudad=Cdiz

    Nombre=Garca, Javier Edad=24 Ciudad=Mlaga

    Nombre=Toledo, Mara Edad=37 Ciudad=Mlaga

    7.2.4 ToLookup + proyector

    public static ILookup ToLookup( this IEnumerable source, Func keySelector, Func elementSelector )

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

    Volviendo al ejemplo de agrupar por ciudades. Nosotros agrupbamos por ciudad, luego le decamos que queramos todas aquellas personas que fueran de Cdiz. El "problema" es que cada objeto que nos devuelve tambin contiene una propiedad del tipo Ciudad lo cual es redundante, puesto que ya sabemos que son de Cdiz.

    Podramos 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 annimo que solo contiene el nombre y la edad descartando la ciudad (pues ya la sabemos a la hora de hacer una consulta).

    Se usara como siempre:

    var gaditanos = perCiudad["Cdiz"];

    Y el resultado:

    Nombre=Rodriguez, Jess Edad=24

    Nombre=Perez, Juan Edad=15

  • 59 Operadores de LINQ

    7.2.5 ToLookup + proyector + comparador personalizado

    public static ILookup ToLookup( this IEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer 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 agrupacin.

    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, Jess Ciudad=Alicante

    Nombre=Perez, Juan Ciudad=Cdiz

  • 60 8. Set (Conjunto)

    8. Set (Conjunto) 8.1 Distinct

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

    8.1.1 Cdigo necesario para los ejemplos

    Una lista de ciudades (con algunas repetidas):

    List ciudades = new List{"Cdiz", "Mlaga", "Murcia", "Mlaga", "Madrid", "Barcelona", "Galicia", "Bilbao", "Cdiz"};

    Un comparador de iniciales:

    public class ComparadorInicial : IEqualityComparer { 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 estndar

    public static IEnumerable Distinct( this IEnumerable source )

    El operador Distinct no toma ningn parmetro, solo la secuencia que usar el operador, pero eso es implcito. Devuelve una enumeracin donde ya no hay entradas repetidas.

    Vemoslo en un ejemplo:

    IEnumerable ciudadesUnicas = ciudades.Distinct();

  • 61 Operadores de LINQ

    Difcil? Claro que no. Como es lgico, la enumeracin contiene:

    Cdiz

    Mlaga

    Murcia

    Madrid

    Barcelona

    Galicia

    Bilbao

    Ninguna ciudad repetida :).

    8.1.3 Distinct + comparador personalizado

    public static IEnumerable Distinct( this IEnumerable source, IEqualityComparer 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 tambin.

    Para ello usaremos el comparador adjunto:

    IEnumerable ciudadesUnicas = ciudades.Distinct( new ComparadorInicial());

    El resultado como cabe de esperar es:

    Cdiz

    Mlaga

    Barcelona

    Galicia

  • 62 8. Set (Conjunto)

    8.2 Except

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

    8.2.1 Cdigo necesario para los ejemplos

    Una lista de ciudades (con algunas repetidas):

    List ciudades = new List{"Cdiz", "Mlaga", "Murcia", "Mlaga", "Madrid", "Barcelona", "Galicia", "Bilbao", "Cdiz"};

    8. Set (Conjunto)

    Una lista de ciudades a descartar:

    List aDescartar = new List { "Galicia", "Murcia" };

    Una lista de iniciales a descartar:

    List inicialDescartar = new List { "C", "M" };

    Un comparador de iniciales:

    public class ComparadorInicial : IEqualityComparer { 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 estndar

    public static IEnumerable Except( this IEnumerable first, IEnumerable second )

    El operador Except toma como parmetro otra secuencia del mismo tipo que usar para descartar elementos. Devuelve una enumeracin con los elementos no descartados.

  • 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. Vemoslo con un ejemplo:

    IEnumerable ciudadesFiltradas = ciudades.Except(aDescartar);

    Tenemos una secuencia que contiene varias ciudades. Empieza descartando las repetidas (Como Mlaga, Cdiz...). 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 quedara:

    Cdiz

    Mlaga

    Madrid

    Barcelona

    Bilbao

    8.2.3 Except + comparador personalizado

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

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

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

    inicialDescartar contiene una C y una M. Segn el comparador que hemos usado, Descartar todas las C y todas las M? No exactamente...

    Recordad cmo trabaja el operador.

  • 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 sern descartadas directamente. As que cuando encuentra Barcelona ya descartar Bilbao por empezar por B tambin. 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 terminara as:

    Barcelona

    Galicia

    Sorprendido? Claro que no, descart todas las que empezaban por C y por M adems de Bilbao puesto que ya haba una que empezara por B (Barcelona).

    8.3 Intersect

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

    8.3.1 Cdigo necesario para los ejemplos

    Una lista de ciudades:

    List ciudades1 = new List{"Cdiz", "Mlaga", "Murcia", "Madrid", "Barcelona", "Galicia", "Bilbao"};

    Otra lista de ciudades:

    List ciudades2 = new List { "Cdiz", "Mlaga", "Bilbao", "Granada", "Pamplona" };

  • 65 Operadores de LINQ

    Un comparador de iniciales:

    public class ComparadorInicial : IEqualityComparer { 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 estndar

    public static IEnumerable Intersect( this IEnumerable first, IEnumerable second )

    Intersect toma como parmetro una secuencia que se usar a la hora de hacer la interseccin. Devuelve una enumeracin con aquellos elementos que coincidan en ambas secuencias.

    Por ejemplo, si queremos crear una enumeracin con las ciudades en comn de las dos listas de ciudades que tenemos:

    IEnumerable ciudades = ciudades1.Intersect(ciudades2);

    Nada difcil, el resultado es:

    Cdiz

    Mlaga

    Bilbao

    Esas tres ciudades coinciden en ambas listas :)

  • 66 8. Set (Conjunto)

    8.3.3 Intersect + comparador personalizado

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

    Como viene siendo costumbre, adems de nuestra secuencia para la interseccin, podemos usar un comparador personalizado.

    Vamos con un ejemplo (es muy estpido lo s, pero los comparadores personalizados se usan en casos muy extremos, y es difcil encontrar ejemplos simples) que nos va ayudar a entender cmo funciona este operador.

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

    Qu debera de devolver? Pinsalo dos veces antes de probarlo.

    Ahora bien, Qu devuelve realmente?:

    Cdiz

    Mlaga

    Barcelona

    Galicia

    Y Bilbao? Y Galicia? Por qu no salen?

    Vamos paso por paso:

    Aqu lo importante es la inicial, nada ms, entonces, ve que en la primera secuencia hay una C y que en la segunda tambin. As que agrega la palabra de la primera secuencia que contiene dicha C, en este caso, Cdiz. 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.

  • 67 Operadores de LINQ

    8.4 Union

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

    8.4.1 Cdigo necesario para los ejemplos

    Una lista de ciudades:

    List ciudades1 = new List{"Cdiz", "Mlaga", "Murcia", "Madrid", "Barcelona", "Galicia", "Bilbao"};

    Otra lista de ciudades:

    List ciudades2 = new List { "Cdiz", "Mlaga", "Bilbao", "Granada", "Pamplona" };

    Un comparador de iniciales:

    public class ComparadorInicial : IEqualityComparer { 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 estndar

    public static IEnumerable Union( this IEnumerable first, IEnumerable second )

    El operador Union toma como parmetro la otra secuencia que usar para la unin. Devuelve una enumeracin con la unin de las dos secuencias.

  • 68 8. Set (Conjunto)

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

    IEnumerable ciudades = ciudades1.Union(ciudades2);

    Nada que deba sorprendernos. El resultado:

    Cdiz

    Mlaga

    Murcia

    Madrid

    Barcelona

    Galicia

    Bilbao

    Granada

    Pamplona

    8.4.3 Union + comparador personalizado

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

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

    Vamos a hacer una unin por iniciales:

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

    Bsicamente crear una enumeracin 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.

  • 69 Operadores de LINQ

    El resultado es:

    Cdiz

    Mlaga

    Barcelona

    Galicia

    Pamplona

  • 70 9. Conversion (Conversin)

    9. Conversion (Conversin)

    9.1 AsEnumerable

    El operador AsEnumerable convierte una secuencia en una enumeracin del tipo IEnumerable.

    9.1.1 Cdigo necesario para los ejemplos

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

    IQueryable 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 AsEnumerable( this IEnumerable source )

    ste operador puede parecer extrao, absurdo. Un operador que quiere transformar una secuencia a un IEnumerable y para ello necesita un... IEnumerable? Los operadores estndar son los de LINQ to Objects y esos son miembros de IEnumerable. 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 consideracin, IQueryable puede tener operadores como... Where, Select, etc. La cosa es que estn 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 estis pensando. Podemos convertir nuestra secuencia IQueryable por una IEnumerable y ya poder usar esos operadores que faltan o las implementaciones de IEnumerable de los operadores.

  • 71 Operadores de LINQ

    Cmo? As:

    IEnumerable ienupersonas = personas.AsEnumerable();

    Con esto hemos convertido de IQueryable a IEnumerable. Hemos podido usar ese mtodo en el IQueryable puesto que esta interfaz implementa IEnumerable.

    9.2 AsQueryable

    El operador AsQueryable convierte una enumeracin del tipo IEnumerable en una del tipo IQueryable

    9.2.1 Cdigo necesario para los ejemplos

    Una tabla de nuestra base de datos convertida a IEnumerable

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

    9.2.2 AsQueryable

    public static IQueryable AsQueryable( this IEnumerable source )

    ste mtodo es justo lo contrario de AsEnumerable. Recomendara leer su descripcin para mejor comprensin. Bsicamente, si tienes un objeto del tipo IEnumerable como por ejemplo el de nuestro cdigo 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 iquerypersonas = ienupersonas.AsQueryable();

    Podramos haber reusado el objeto personas pero era para dar otro ejemplo.

  • 72 9. Conversion (Conversin)

    9.3 Cast

    El operador Cast puede convertir un array estndar a un objeto que implementa IEnumerable.

    9.3.1 Cdigo necesario para los ejemplos

    Un array de enteros:

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

    9.3.2 Cast

    public static IEnumerable Cast( this IEnumerable source )

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

    IEnumerable enuNum = arrayNum.Cast();

    El resultado es:

    1 2 3 4 5

    Ahora la pregunta es: Para qu queremos convertir el array a un IEnumerable? Hay una razn sencilla: Un array normal y corriente no implementa IEnumerable lo que conlleva a no poder usar ningn operador de LINQ. As que si convertimos nuestro array en un IEnumerable pues podremos aplicar algn que otro operador, por ejemplo:

    IEnumerable enuNum = arrayNum.Cast().Select(i => i * i); Ahora gracias a Cast hemos podido aplicarle un Select.

  • 73 Operadores de LINQ

    El resultado de esto ltimo sera:

    1 4 9 16 25

    9.4 ToArray

    El operador ToArray convierte una enumeracin del tipo IEnumerable en un array.

    9.4.1 Cdigo 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 personas = new List { new Persona("Rodriguez, Jess", 24, "Cdiz"), new Persona("Bautista, Jess", 15, "Alicante"), new Persona("Perez, Juan", 15, "Cdiz"), new Persona("Garca, Javier", 24, "Mlaga"), new Persona("Toledo, Mara", 37, "Mlaga") };

  • 74 9. Conversion (Conversin)

    9.4.2 ToArray

    public static TSource[] ToArray( this IEnumerable source )

    ste operador toma como parmetro un objeto IEnumerable 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 nombres = personas.Select(p => p.Nombre); string[] arrayNombres = nombres.ToArray();

    Como ves, proyecto solo los nombres en una enumeracin de string y luego convierto dicha enumeracin en un array de string. Se puede reducir a un paso tambin:

    string[] nombres = personas.Select(p => p.Nombre).ToArray(); En ambos casos, se imprimiran los nombres:

    Rodriguez, Jess Bautista, Jess Perez, Juan Garca, Javier Toledo, Mara

    9.5 ToDictionary

    El operador ToDictionary convierte una enumeracin del tipo IEnumerable en un Dictionary.

    9.5.1 Cdigo 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; }

  • 75 Operadores de LINQ

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

    Una lista de personas:

    List personas = new List { new Persona("Rodriguez, Jess", "24", "Cdiz"), new Persona("Bautista, Jess", "15", "Alicante"), new Persona("Perez, Juan", "16", "Cdiz"), new Persona("Garca, Javier", "26", "Mlaga"), new Persona("Toledo, Mara", "37", "Mlaga") };

    Un comparador personalizado:

    public class StringANumero : IEqualityComparer { 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 estndar

    public static Dictionary ToDictionary( this IEnumerable source, Func keySelector )

    ToDictionary recibe un delegado que usaremos para seleccionar la clave. Devuelve un diccionario cuyas claves son las que hemos seleccionado e irn 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.

  • 76 9. Conversion (Conversin)

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

    Dictionary dictNombres = personas.ToDictionary( p => p.Nombre);

    Y tal como pasa con ILookup podemos hacer un:

    Persona jesus = dictNombres["Rodriguez, Jess"];

    9.5.3 ToDictionary + comparador personalizado

    public static Dictionary ToDictionary( this IEnumerable source, Func keySelector, IEqualityComparer 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 dictEdad = personas.ToDictionary( p => p.Edad, new StringANumero());

    Nada nuevo, usamos nuestro comparador. Ahora bien, quiero recoger del diccionario la persona de 24 aos, o sea, Jess:

    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 travs de nuestro comparador personalizado. Entonces, el mtodo 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 quedara simplemente como 24, claro, cuando llega a la entrada de Jess ve que su edad es 24 tambin y la da por vlida.

  • 77 Operadores de LINQ

    9.5.4 ToDictionary + proyector elemento

    public static Dictionary ToDictionary( this IEnumerable source, Func keySelector, Func elementSelector )

    Ahora podemos definir cul ser la clave y como sern 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 ms:

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

    Si imprimiramos el diccionario, conseguiramos algo as:

    [Rodriguez, Jess, Cdiz] [Bautista, Jess, Alicante] [Perez, Juan, Cdiz] [Garca, Javier, Mlaga] [Toledo, Mara, Mlaga]

    9.5.5 ToDictionary + proyector elemento + comparador personalizado

    public static Dictionary ToDictionary( this IEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer comparer )

    Adems 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 ms:

    Dictionary 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

  • 78 9. Conversion (Conversin)

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

    9.6 ToList

    El operador ToList convierte una enumeracin del tipo IEnumerable en un objeto del tipo List.

    9.6.1 Cdigo 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 personas = new List { new Persona("Rodriguez, Jess", 24, "Cdiz"), new Persona("Bautista, Jess", 15, "Alicante"), new Persona("Perez, Juan", 15, "Cdiz"), new Persona("Garca, Javier", 24, "Mlaga"), new Persona("Toledo, Mara", 37, "Mlaga") };

    9.6.2 ToList

    public static List ToList( this IEnumerable source )

    Recibe como parmetro el objeto tipo IEnumerable a convertir. Devuelve un objeto List de dicho tipo.

  • 79 Operadores de LINQ

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

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

    List nombresOrdenados = orderYSelect.ToList();

    O lo que es lo mismo:

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

    El resultado de imprimir esto sera:

    Bautista, Jess Garca, Javier Perez, Juan Rodriguez, Jess Toledo, Mara

  • 80 10. Equality (Igualdad)

    10. Equality (Igualdad)

    10.1 SequenceEqual

    El operador SequenceEqual sirve para comprobar la igualdad entre dos secuencias

    10.1.1 Cdigo necesario para los ejemplos

    Dos listas con el mismo contenido:

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

    Una lista con contenido distinto:

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

    Un comparador personalizado:

    public class ComparadorInicial : IEqualityComparer { 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 estndar

    public static bool SequenceEqual( this IEnumerable first, IEnumerable second )

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

  • 81 Opera