TOC

This article has been localized into Spanish by the community.

LINQ.:

Agrupación de datos: el método GroupBy ().

Hasta ahora, hemos trabajado principalmente con listas de datos. Lo hemos ordenado, limitado y moldeado en nuevos objetos, pero todavía falta una operación importante: la agrupación de datos. Cuando agrupa datos, toma una lista de algo y luego la divide en varios grupos, en función de una o varias propiedades. Solo imagine que tenemos una fuente de datos como esta:

var users = new List<User>()
{
    new User { Name = "John Doe", Age = 42, HomeCountry = "USA" },
    new User { Name = "Jane Doe", Age = 38, HomeCountry = "USA" },
    new User { Name = "Joe Doe", Age = 19, HomeCountry = "Germany" },
    new User { Name = "Jenna Doe", Age = 19, HomeCountry = "Germany" },
    new User { Name = "James Doe", Age = 8, HomeCountry = "USA" },
};

Una lista plana de objetos de usuario, pero podría ser interesante agrupar a estos usuarios en, p. su país de origen o su edad. Con LINQ, esto es muy fácil, aunque el uso del método GroupBy () puede ser un poco confuso al principio. Echemos un vistazo a cómo funciona:

using System;    
using System.Collections.Generic;    
using System.Linq;    

namespace LinqGroup    
{    
    class Program    
    {    
static void Main(string[] args)    
{    
    var users = new List<User>()    
    {    
new User { Name = "John Doe", Age = 42, HomeCountry = "USA" },    
new User { Name = "Jane Doe", Age = 38, HomeCountry = "USA" },    
new User { Name = "Joe Doe", Age = 19, HomeCountry = "Germany" },    
new User { Name = "Jenna Doe", Age = 19, HomeCountry = "Germany" },    
new User { Name = "James Doe", Age = 8, HomeCountry = "USA" },    
    };    
    var usersGroupedByCountry = users.GroupBy(user => user.HomeCountry);    
    foreach(var group in usersGroupedByCountry)    
    {    
Console.WriteLine("Users from " + group.Key + ":");    
foreach(var user in group)    
Console.WriteLine("* " + user.Name);
    }    
}    

public class User    
{    
    public string Name { get; set; }    

    public int Age { get; set; }    

    public string HomeCountry { get; set; }    
}    
    }    
}

El resultado resultante se verá más o menos así:

Users from USA:
* John Doe
* Jane Doe
* James Doe
Users from Germany:
* Joe Doe
* Jenna Doe

El ejemplo puede parecer un poco largo, pero como pronto se dará cuenta, la mayor parte es solo preparar la fuente de datos. Recuerde que todos los datos también podrían provenir de un documento XML o una base de datos; es más fácil demostrar con un origen de datos de objetos que puede usar tal cual.

La parte interesante es cuando creamos la variable usersGroupedByCountry. Lo hacemos llamando al método GroupBy () en nuestro origen de datos, proporcionando el parámetro por el que queremos agrupar los datos. En este caso, quiero que los usuarios estén agrupados por su país de origen, por lo que esa es la propiedad que proporciono al método GroupBy(). El resultado es un objeto con una propiedad Key, que contiene el valor de la propiedad que agrupamos (HomeCountry, en este caso), así como todos los objetos que pertenecen al grupo. Usamos eso en las siguientes líneas para iterar sobre los grupos que acabamos de crear, y para cada grupo, imprimimos la Clave (HomeCountry) y luego iteramos e imprimimos todos los objetos de Usuario del grupo.

Teclas de grupo personalizadas.

Como puede ver, la agrupación por una propiedad existente es fácil, pero como ya habrá sabido, los métodos LINQ son muy flexibles. Es igual de simple crear sus propios grupos personalizados, en función de lo que quiera; un ejemplo de eso podría ser el siguiente, donde creamos grupos basados ​​en las dos primeras letras del nombre del usuario:

using System;
using System.Collections.Generic;
using System.Linq;

namespace LinqGroup
{
    class Program
    {
static void Main(string[] args)
{
    var users = new List<User>()
    {
new User { Name = "John Doe", Age = 42, HomeCountry = "USA" },
new User { Name = "Jane Doe", Age = 38, HomeCountry = "USA" },
new User { Name = "Joe Doe", Age = 19, HomeCountry = "Germany" },
new User { Name = "Jenna Doe", Age = 19, HomeCountry = "Germany" },
new User { Name = "James Doe", Age = 8, HomeCountry = "USA" },
    };
    var usersGroupedByFirstLetters = users.GroupBy(user => user.Name.Substring(0, 2));
    foreach(var group in usersGroupedByFirstLetters)
    {
Console.WriteLine("Users starting with " + group.Key + ":");
foreach(var user in group)
    Console.WriteLine("* " + user.Name);
    }
}

public class User
{
    public string Name { get; set; }

    public int Age { get; set; }

    public string HomeCountry { get; set; }
}
    }
}

Simplemente llamamos al método Substring () en el nombre, para obtener las dos primeras letras, y luego LINQ crea los grupos de usuarios basados ​​en él. El resultado se verá más o menos así:

Users starting with Jo:
* John Doe
* Joe Doe
Users starting with Ja:
* Jane Doe
* James Doe
Users starting with Je:
* Jenna Doe

Como puede ver, somos libres de llamar a un método dentro del método GroupBy (); de hecho, podemos hacer casi todo lo que queramos allí, siempre que devolvamos algo que LINQ pueda usar para agrupar los elementos. Incluso podemos crear un método que devuelva una nueva información sobre el elemento y luego usarlo para crear un grupo, como lo hacemos en el siguiente ejemplo:

using System;
using System.Collections.Generic;
using System.Linq;

namespace LinqGroup
{
    class Program
    {
static void Main(string[] args)
{
    var users = new List<User>()
    {
new User { Name = "John Doe", Age = 42, HomeCountry = "USA" },
new User { Name = "Jane Doe", Age = 38, HomeCountry = "USA" },
new User { Name = "Joe Doe", Age = 19, HomeCountry = "Germany" },
new User { Name = "Jenna Doe", Age = 19, HomeCountry = "Germany" },
new User { Name = "James Doe", Age = 8, HomeCountry = "USA" },
    };
    var usersGroupedByAgeGroup = users.GroupBy(user => user.GetAgeGroup());
    foreach(var group in usersGroupedByAgeGroup)
    {
Console.WriteLine(group.Key + ":");
foreach(var user in group)
    Console.WriteLine("* " + user.Name + " [" + user.Age + " years]");
    }
}

public class User
{
    public string Name { get; set; }

    public int Age { get; set; }

    public string HomeCountry { get; set; }

    public string GetAgeGroup()
    {
if (this.Age < 13)
    return "Children";
if (this.Age < 20)
    return "Teenagers";
return "Adults";
    }
}
    }
}

Observe cómo he implementado un método GetAgeGroup() en la clase Usuario. Devuelve una cadena que define el grupo de edad del usuario y simplemente lo llamamos en el método GroupBy () para usarlo como clave de grupo. El resultado se verá así:

Adults:
* John Doe [42 years]
* Jane Doe [38 years]
Teenagers:
* Joe Doe [19 years]
* Jenna Doe [19 years]
Children:
* James Doe [8 years]

Elijo implementar el método GetAgeGroup () en la clase Usuario, porque podría ser útil en otros lugares, pero a veces solo necesitas una lógica rápida para crear los grupos, para no ser reutilizado en otro lugar. En esas situaciones, puede proporcionar la lógica directamente al método GroupBy (), como una expresión lambda, como esta:

var usersGroupedByAgeGroup = users.GroupBy(user =>
    {
if (user.Age < 13)
    return "Children";
if (user.Age < 20)
    return "Teenagers";
return "Adults";
    });

¡El resultado es, por supuesto, el mismo!

Agrupación por una clave compuesta.

Hasta ahora, las claves de nuestros grupos solo han sido un valor único, p.e. una propiedad o el resultado de una llamada a un método. Sin embargo, puede crear sus propias claves que contienen varios valores, que se denominan claves compuestas. Un ejemplo de uso podría ser si quisiéramos agrupar a nuestros usuarios según su país de origen y su edad, de esta manera:

using System;
using System.Collections.Generic;
using System.Linq;

namespace LinqGroup2
{
    class Program
    {
static void Main(string[] args)
{
    var users = new List<User>()
    {
new User { Name = "John Doe", Age = 42, HomeCountry = "USA" },
new User { Name = "Jane Doe", Age = 38, HomeCountry = "USA" },
new User { Name = "Joe Doe", Age = 19, HomeCountry = "Germany" },
new User { Name = "Jenna Doe", Age = 19, HomeCountry = "Germany" },
new User { Name = "James Doe", Age = 8, HomeCountry = "USA" },
    };

    var usersGroupedByCountryAndAge = users.GroupBy(user => new { user.HomeCountry, user.Age });
    foreach(var group in usersGroupedByCountryAndAge)
    {
Console.WriteLine("Users from " + group.Key.HomeCountry + " at the age of " + group.Key.Age + ":");
foreach (var user in group)
    Console.WriteLine("* " + user.Name + " [" + user.Age + " years]");
    }
}

public class User
{
    public string Name { get; set; }

    public int Age { get; set; }

    public string HomeCountry { get; set; }

}
    }
}

Observe la sintaxis que usamos en el método GroupBy (): en lugar de proporcionar una sola propiedad, creamos un nuevo objeto anónimo, que contiene las propiedades HomeCountry y Age. LINQ ahora creará grupos basados ​​en estas dos propiedades y adjuntará el objeto anónimo a la propiedad Key del grupo. Somos libres de usar ambas propiedades cuando iteramos sobre los grupos, como puede ver. El resultado se verá más o menos así:

Users from USA at the age of 42:
* John Doe [42 years]
Users from USA at the age of 38:
* Jane Doe [38 years]
Users from Germany at the age of 19:
* Joe Doe [19 years]
* Jenna Doe [19 years]
Users from USA at the age of 8:
* James Doe [8 years]

Como siempre, hemos utilizado la sintaxis del método LINQ a través de este artículo, pero permítame proporcionarle un ejemplo comparativo sobre cómo se podría hacer con la sintaxis LINQ Query:

// Method syntax
var usersGroupedByCountryAndAge = users.GroupBy(user => new { user.HomeCountry, user.Age });
// Query syntax
var usersGroupedByCountryAndAgeQ = from user in users group user by new { user.HomeCountry, user.Age } into userGroup select userGroup;

Resumen.

Como probablemente pueda ver en los ejemplos de este artículo, el método GroupBy () de LINQ es extremadamente poderoso. Realmente le permite usar sus datos de nuevas maneras, con muy poco código. Anteriormente, esto sería muy engorroso o requeriría una base de datos relacional, pero con LINQ, puede usar cualquier fuente de datos que desee y aun así obtener la misma funcionalidad fácil de usar.


This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!