Sortare în LINQ
Operatorul orderby și metoda OrderBy
Pentru sortarea unui set de date în LINQ, se poate utiliza operatorul orderby:
int[] numbers = { 3, 12, 4, 10 };
var orderedNumbers = from i in numbers
orderby i
select i;
foreach (int i in orderedNumbers)
Console.WriteLine(i);
Operatorul orderby acceptă un criteriu de sortare. În acest caz, criteriul este însăși numărul. Rezultatul execuției programului:
3
4
10
12
Dacă numerele sunt sortate în mod standard, cum este acceptat în matematică, atunci șirurile sunt sortate în ordine alfabetică:
string[] people = { "Tom", "Bob", "Sam" };
var orderedPeople = from p in people orderby p select p;
foreach (var p in orderedPeople)
Console.WriteLine(p); // Bob Sam Tom
În locul operatorului orderby, se poate utiliza metoda de extensie OrderBy():
OrderBy (Func<TSource,TKey> keySelector)
OrderBy (Func<TSource,TKey> keySelector, IComparer<TKey>? comparer);
Prima versiune a metodei primește un delegat care, prin parametrul său, primește un element al colecției și returnează valoarea utilizată pentru sortare. A doua versiune permite, de asemenea, specificarea principiului de sortare prin implementarea interfeței IComparer.
Rescriem exemplele anterioare folosind metoda OrderBy:
int[] numbers = { 3, 12, 4, 10 };
var orderedNumbers = numbers.OrderBy(n => n);
foreach (int i in orderedNumbers)
Console.WriteLine(i);
string[] people = { "Tom", "Bob", "Sam" };
var orderedPeople = people.OrderBy(p => p);
foreach (var p in orderedPeople)
Console.WriteLine(p);
Sortarea obiectelor complexe
Să luăm un exemplu mai complex. Să presupunem că trebuie să sortăm un set de obiecte complexe. Atunci, ca și criteriu, putem specifica o proprietate a clasei obiectului:
var people = new List<Person>
{
new Person("Tom", 37),
new Person("Sam", 28),
new Person("Tom", 22),
new Person("Bob", 41),
};
// folosind operatorul orderby
var sortedPeople1 = from p in people
orderby p.Name
select p;
foreach (var p in sortedPeople1)
Console.WriteLine($"{p.Name} - {p.Age}");
// folosind metoda OrderBy
var sortedPeople2 = people.OrderBy(p => p.Name);
foreach (var p in sortedPeople2)
Console.WriteLine($"{p.Name} - {p.Age}");
record class Person(string Name, int Age);
Sortarea în ordine crescătoare și descrescătoare
Implicit, operatorul orderby și metoda OrderBy sortează în ordine crescătoare. Cu ajutorul cuvintelor cheie ascending (sortare crescătoare) și descending (sortare descrescătoare) pentru operatorul orderby se poate specifica explicit direcția de sortare. De exemplu, să sortăm un array de numere în ordine descrescătoare:
int[] numbers = { 3, 12, 4, 10 };
var orderedNumbers = from i in numbers
orderby i descending
select i;
foreach (int i in orderedNumbers)
Console.WriteLine(i); // 12 10 4 3
Pentru sortarea în ordine descrescătoare se poate folosi metoda OrderByDescending(), care funcționează similar cu OrderBy, cu excepția direcției de sortare:
int[] numbers = { 3, 12, 4, 10 };
var orderedNumbers = numbers.OrderByDescending(n => n);
foreach (int i in orderedNumbers)
Console.WriteLine(i); // 12 10 4 3
Criterii multiple de sortare
În seturile de obiecte complexe apare uneori situația în care trebuie să sortăm nu doar după un câmp, ci după mai multe câmpuri simultan. Pentru aceasta, în interogarea LINQ, toate criteriile se specifică în ordinea priorității, separate prin virgulă:
var people = new List<Person>
{
new Person("Tom", 37),
new Person("Sam", 28),
new Person("Tom", 22),
new Person("Bob", 41),
};
// folosind operatorul orderby
var sortedPeople1 = from p in people
orderby p.Name, p.Age
select p;
foreach (var p in sortedPeople1)
Console.WriteLine($"{p.Name} - {p.Age}");
Rezultatul programului:
Bob - 41
Sam - 28
Tom - 22
Tom - 37
Pentru criterii de sortare diferite, se poate stabili direcția:
// folosind operatorul orderby
var sortedPeople1 = from p in people
orderby p.Name, p.Age descending // sortare descrescătoare după vârstă
select p;
Cu ajutorul metodelor de extensie, același lucru se poate face folosind metoda ThenBy() (pentru sortare crescătoare) și ThenByDescending() (pentru sortare descrescătoare):
var sortedPeople2 = people.OrderBy(p => p.Name).ThenByDescending(p => p.Age);
Rezultatul va fi similar cu cel anterior.
Suprascrierea criteriului de sortare
Cu ajutorul implementării IComparer se pot suprascrie criteriile de sortare dacă acestea nu ne satisfac. De exemplu, șirurile sunt sortate implicit în ordine alfabetică. Dar ce facem dacă vrem să sortăm șirurile în funcție de lungimea lor? Să rezolvăm această problemă:
string[] people = new[] { "Kate", "Tom", "Sam", "Mike", "Alice" };
var sortedPeople = people.OrderBy(p => p, new CustomStringComparer());
foreach (var p in sortedPeople)
Console.WriteLine(p);
// comparare după lungimea șirului
class CustomStringComparer : IComparer<string>
{
public int Compare(string? x, string? y)
{
int xLength = x?.Length ?? 0; // dacă x este null, atunci lungimea este 0
int yLength = y?.Length ?? 0;
return xLength - yLength;
}
}
Interfața IComparer este tipizată cu tipul datelor sortate (în acest caz, tipul string). Pentru a implementa această interfață, trebuie să definim metoda Compare. Aceasta returnează un număr: dacă primul parametru este mai mare decât al doilea, numărul este mai mare de 0; dacă este mai mic, numărul este mai mic de 0.
Dacă ambii parametri sunt egali, se returnează 0.
În acest caz, dacă parametrul este null, vom considera că lungimea șirului este 0. Și cu ajutorul diferenței dintre lungimile șirurilor din ambii parametri determinăm care dintre ele este mai mare.
Rezultatul programului:
Tom
Sam
Kate
Mike
Alice