OrderBy / ThenBy în C# (.NET)

LINQ (Language Integrated Query) .NET 3.5+ (Take(Range) și MaxBy în .NET 6+)

Sortează secvențele crescător sau descrescător pe baza unuia sau mai multor criterii și extrage pagini cu Skip și Take.

Sintaxă C#
var sortate = colectie.OrderBy(x => x.Categorie).ThenByDescending(x => x.Pret);

Ce face OrderBy / ThenBy

Metodele OrderBy și OrderByDescending sortează elementele unei secvențe fără a modifica colecția originală, returnând o interfață specializată IOrderedEnumerable<T>. Atunci când două elemente au valori egale pentru primul criteriu de sortare, poți înlănțui unul sau mai multe criterii secundare prin ThenBy și ThenByDescending.

După stabilirea ordinii de sortare, metodele Skip(n) și Take(m) sunt folosite standard pentru implementarea paginării (de exemplu, pagina p cu câte k elemente pe pagină se obține prin .Skip((p - 1) * k).Take(k)). Dacă ai nevoie doar de elementul cu valoarea maximă sau minimă după o proprietate (fără a sorta toată lista în O(n log n)), metodele MaxBy și MinBy (introduse în .NET 6) îl găsesc într-o singură parcurgere liniară O(n).

Parametri, metode și opțiuni

Parametru / Metodă Descriere
OrderBy(x => cheie) / OrderByDescending Sortează secvența crescător sau descrescător după cheia selectată.
ThenBy(x => cheie2) / ThenByDescending Aplică un criteriu secundar de departajare pentru elementele egale după sortarea precedentă.
Order() / OrderDescending() Sortează direct o secvență de tipuri primitive (int, string) fără a mai scrie x => x (.NET 7+).
Skip(int count) / Take(int count) Sare peste primele count elemente, respectiv păstrează cel mult count elemente (ideal pentru paginare).
MaxBy(x => cheie) / MinBy(x => cheie) Returnează în timp O(n) întregul obiect care are valoarea maximă sau minimă pentru proprietatea specificată (.NET 6+).

Exemple

Exemplul 1: Sortare multi-criteriu cu OrderBy și ThenByDescending

Sortăm articolele alfabetic după categorie (crescător), iar în cadrul aceleiași categorii după numărul de vizualizări (descrescător).

var articole = new[]
{
    new Articol("Introducere în LINQ", "C#", 4200),
    new Articol("Indexuri B-Tree", "PostgreSQL", 3100),
    new Articol("Async și Await explicat", "C#", 8900),
    new Articol("Partiționare tabele", "PostgreSQL", 5400)
};

var clasament = articole
    .OrderBy(a => a.Categorie)
    .ThenByDescending(a => a.Vizualizari);

foreach (var a in clasament)
{
    Console.WriteLine($"[{a.Categorie}] {a.Titlu} ({a.Vizualizari} afișări)");
}

Articol? celMaiCitit = articole.MaxBy(a => a.Vizualizari);
Console.WriteLine($"Top absolut: {celMaiCitit?.Titlu}");

record Articol(string Titlu, string Categorie, int Vizualizari);

Ieșire în consolă

[C#] Async și Await explicat (8900 afișări)
[C#] Introducere în LINQ (4200 afișări)
[PostgreSQL] Partiționare tabele (5400 afișări)
[PostgreSQL] Indexuri B-Tree (3100 afișări)
Top absolut: Async și Await explicat

Exemplul 2: Paginare cu Skip și Take

Extragem pagina a 2-a (câte 3 elemente pe pagină) dintr-o listă ordonată.

int[] scoruri = [45, 92, 78, 100, 64, 88, 95, 71];

int pagina = 2;
int pePagina = 3;

var pagina2 = scoruri
    .OrderDescending()
    .Skip((pagina - 1) * pePagina)
    .Take(pePagina);

Console.WriteLine($"Pagina {pagina} (locurile 4-6): " + string.Join(", ", pagina2));

Ieșire în consolă

Pagina 2 (locurile 4-6): 88, 78, 71

Greșeli frecvente

  • Înlănțuirea a două apeluri .OrderBy(x => x.A).OrderBy(x => x.B) în loc de .OrderBy(x => x.A).ThenBy(x => x.B) — al doilea apel OrderBy anulează complet prima sortare și reordonează întreaga listă doar după criteriul B!
  • Sortarea întregii colecții cu .OrderByDescending(x => x.Pret).FirstOrDefault() doar pentru a găsi cel mai scump produs — folosește în schimb .MaxBy(x => x.Pret) (.NET 6+), care este mai expresiv și rulează în O(n) în loc de O(n log n).

Funcții și concepte înrudite

  • Enumerable.Where: Filtrează declarativ o secvență de elemente pe baza unui predicat lambda, cu execuție amânată (deferred execution).
  • Enumerable.Select: Proiectează (transformă) fiecare element al unei secvențe într-o formă nouă și aplatizează colecțiile imbricate cu SelectMany.
  • GroupBy: Grupează elementele unei secvențe după o cheie comună pentru a calcula statistici, sume și agregări pe categorii.