Gruparea datelor
Pentru gruparea datelor după anumite criterii se utilizează operatorul group by și metoda GroupBy().
Operatorul group by
Să presupunem că avem un set de obiecte de tipul următor:
record class Person(string Name, string Company);
Această clasă reprezintă un utilizator și are două proprietăți: Name (numele utilizatorului) și Company (compania unde lucrează utilizatorul). Să grupăm setul de utilizatori după companie:
Person[] people =
{
new Person("Tom", "Microsoft"), new Person("Sam", "Google"),
new Person("Bob", "JetBrains"), new Person("Mike", "Microsoft"),
new Person("Kate", "JetBrains"), new Person("Alice", "Microsoft"),
};
var companies = from person in people
group person by person.Company;
foreach (var company in companies)
{
Console.WriteLine(company.Key);
foreach (var person in company)
{
Console.WriteLine(person.Name);
}
Console.WriteLine(); // pentru separarea între grupuri
}
Dacă în expresia LINQ ultimul operator care efectuează operațiuni asupra selecției este group, atunci operatorul select nu se folosește.
Operatorul group primește criteriul după care se face gruparea:
group person by person.Company
În acest caz, gruparea se face după proprietatea Company. Rezultatul operatorului group este o selecție care constă din grupuri. Fiecare grup reprezintă un obiect IGrouping<K, V>, unde K indică tipul cheii - tipul proprietății după care se face gruparea (aici este de tip string). Iar V reprezintă tipul obiectelor grupate - în acest caz grupăm obiecte de tip Person.
Fiecare grup are o cheie, pe care o putem obține prin proprietatea Key: g.Key. Aici va fi numele companiei.
Toate elementele din grup pot fi obținute printr-o iterație suplimentară. Elementele grupului au același tip ca și tipul obiectelor care au fost transmise operatorului group, adică în acest caz obiecte de tip Person.
Rezultatul afișării:
Microsoft
Tom
Mike
Alice
Google
Sam
JetBrains
Bob
Kate
Metoda GroupBy
Ca alternativă, se poate folosi metoda de extensie GroupBy. Aceasta are mai multe suprascrieri, să luăm cea mai simplă dintre ele:
GroupBy<TSource, TKey>(Func<TSource, TKey> keySelector);
Această versiune primește un delegat care, ca parametru, primește fiecare element al colecției și returnează criteriul de grupare.
Rescriem exemplul anterior folosind metoda GroupBy:
Person[] people =
{
new Person("Tom", "Microsoft"), new Person("Sam", "Google"),
new Person("Bob", "JetBrains"), new Person("Mike", "Microsoft"),
new Person("Kate", "JetBrains"), new Person("Alice", "Microsoft"),
};
var companies = people.GroupBy(p => p.Company);
foreach (var company in companies)
{
Console.WriteLine(company.Key);
foreach (var person in company)
{
Console.WriteLine(person.Name);
}
Console.WriteLine(); // pentru separarea între grupuri
}
Crearea unui nou obiect în timpul grupării
Să modificăm interogarea și să creăm un nou obiect din grup:
Person[] people =
{
new Person("Tom", "Microsoft"), new Person("Sam", "Google"),
new Person("Bob", "JetBrains"), new Person("Mike", "Microsoft"),
new Person("Kate", "JetBrains"), new Person("Alice", "Microsoft"),
};
var companies = from person in people
group person by person.Company into g
select new { Name = g.Key, Count = g.Count() };
foreach (var company in companies)
{
Console.WriteLine($"{company.Name} : {company.Count}");
}
Expresia:
group person by person.Company into g
definește variabila g, care va conține grupul. Cu ajutorul acestei variabile putem apoi crea un nou obiect de tip anonim (deși putem defini o nouă clasă pentru această sarcină):
select new { Name = g.Key, Count = g.Count() }
Acum, rezultatul interogării LINQ va reprezenta un set de obiecte de tip anonim, cu două proprietăți Name și Count.
Rezultatul programului:
Microsoft : 3
Google : 1
JetBrains : 2
O operație similară folosind metoda GroupBy:
var companies = people
.GroupBy(p => p.Company)
.Select(g => new { Name = g.Key, Count = g.Count() });
Interogări imbricate
De asemenea, putem realiza interogări imbricate:
Person[] people =
{
new Person("Tom", "Microsoft"), new Person("Sam", "Google"),
new Person("Bob", "JetBrains"), new Person("Mike", "Microsoft"),
new Person("Kate", "JetBrains"), new Person("Alice", "Microsoft"),
};
var companies = from person in people
group person by person.Company into g
select new
{
Name = g.Key,
Count = g.Count(),
Employees = from p in g select p
};
foreach (var company in companies)
{
Console.WriteLine($"{company.Name} : {company.Count}");
foreach (var employee in company.Employees)
{
Console.WriteLine(employee.Name);
}
Console.WriteLine(); // pentru separarea între grupuri
}
Aici, proprietatea Employees a fiecărui grup este formată cu ajutorul unei interogări suplimentare care selectează toți utilizatorii din acel grup. Rezultatul afișării:
Microsoft : 3
Tom
Mike
Alice
Google : 1
Sam
JetBrains : 2
Bob
Kate
O interogare similară folosind metoda GroupBy:
var companies = people
.GroupBy(p => p.Company)
.Select(g => new
{
Name = g.Key,
Count = g.Count(),
Employees = g.Select(p => p)
});