Laporan yang urutannya berubah-ubah sulit dibandingkan antar pemanggilan. Operator ordering menata elemen berdasarkan satu kunci primer dan, bila perlu, kunci sekunder. C# menyediakan empat operator untuk keperluan ini, dan urutan elemen berkunci sama tetap terjaga secara stabil.

Kunci Primer dan Kunci Sekunder #

Contoh berikut mengurutkan produk menurut kategori, lalu menurut harga dari yang terbesar:

csharp
record Produk(string Nama, string Kategori, int Harga, int Stok);

List<Produk> produk = new()
{
    new Produk("Kopi Gayo", "Minuman", 25000, 40),
    new Produk("Teh Melati", "Minuman", 15000, 60),
    new Produk("Roti Bakar", "Makanan", 12000, 25),
    new Produk("Susu UHT", "Minuman", 20000, 35),
    new Produk("Keripik Singkong", "Makanan", 10000, 80)
};

var urut = produk
    .OrderBy(p => p.Kategori)
    .ThenByDescending(p => p.Harga);

foreach (var p in urut)
{
    Console.WriteLine($"{p.Kategori}: {p.Nama} ({p.Harga})");
}

Output yang Dihasilkan:

text
Makanan: Roti Bakar (12000)
Makanan: Keripik Singkong (10000)
Minuman: Kopi Gayo (25000)
Minuman: Susu UHT (20000)
Minuman: Teh Melati (15000)

Mari kita bedah baris demi baris:

  1. .OrderBy(p => p.Kategori): kunci primer; kategori disusun menaik secara alfabetis, sehingga Makanan tampil sebelum Minuman.
  2. .ThenByDescending(p => p.Harga): kunci sekunder; di dalam kategori yang sama, harga terbesar tampil lebih dulu.
  3. stabilitas urutan: produk dengan kunci sama tetap mempertahankan urutan asli sumbernya.
  4. padanan query expression: gabungan ini ditulis orderby p.Kategori, p.Harga descending dalam satu klausa.

Empat Operator Ordering #

OperatorArahPeran
OrderBymenaikkunci primer
OrderByDescendingmenurunkunci primer
ThenBymenaikkunci sekunder dan seterusnya
ThenByDescendingmenurunkunci sekunder dan seterusnya

Klausa ThenBy* hanya dipakai setelah OrderBy*, karena kunci sekunder bermakna bila kunci primer menghasilkan grup yang sama. Untuk membalik urutan hasil apa adanya tanpa memedulikan kunci, tersedia method Reverse().

Seluruh operator ordering juga menerima IComparer<T> sebagai argumen kedua, misalnya OrderBy(p => p.Nama, StringComparer.OrdinalIgnoreCase) untuk mengabaikan perbedaan huruf besar-kecil. Tanpa comparer khusus, perbandingan string mengikuti aturan bawaan tipe string, dan perilaku itu menentukan hasil antara nama yang berawalan huruf besar maupun kecil.

Setiap panggilan OrderBy* menelusuri seluruh sumber, sehingga pengurutan termasuk operator yang paling berat; bab LINQ Performance mengingatkan agar kita tidak memanggilnya tanpa kebutuhan.