Sintaks Metode LINQ

Where, Select, OrderBy, serta First vs FirstOrDefault.

Daftar isi

Sintaks metode LINQ (Language Integrated Query) membentuk query dengan memanggil metode ekstensi pada Enumerable secara berantai, dan setiap metode menerima lambda, yaitu fungsi ringkas yang ditulis tanpa nama. Topik ini membahas empat operator yang paling sering digunakan, yaitu Where untuk menyaring, Select untuk mengubah bentuk, OrderBy untuk mengurutkan, serta pasangan First dan FirstOrDefault untuk mengambil satu elemen. Setiap operator disertai contoh yang dijalankan dan keluaran aslinya.

Where untuk menyaring#

Metode Where menerima lambda yang disebut predicate, yaitu fungsi yang mengembalikan true atau false untuk setiap elemen, kemudian menghasilkan barisan yang hanya memuat elemen dengan predikat true. Sumbernya tidak berubah, karena Where mengembalikan barisan baru yang menelusuri sumbernya pada saat penelusuran dilakukan.

csharp Contoh: Where menyaring nilai yang memenuhi ambang lulus
// Contoh: Where menyaring nilai ujian yang memenuhi ambang lulus
List<int> nilaiUjian = new() { 84, 60, 90, 55, 75 };
IEnumerable<int> lulus = nilaiUjian.Where(n => n >= 75);
Console.WriteLine(string.Join(", ", lulus));
text Keluaran
84, 90, 75

Lambda n => n >= 75 dibaca sebagai fungsi yang menerima n lalu mengembalikan hasil n >= 75. Barisan lulus memuat tiga nilai karena 60 dan 55 tidak memenuhi ambang tersebut. Beberapa pemanggilan Where dapat dirangkai, dan hasilnya setara dengan satu predikat yang menggabungkan semua syarat dengan operator logika.

Operator lain dalam keluarga yang sama sering berdampingan dengan Where. Metode Count() menghitung banyaknya elemen yang lolos, Any() memeriksa apakah ada satu elemen pun yang lolos, sedangkan Take(n) dan Skip(n) memotong barisan dari depan. Seluruhnya bekerja pada barisan yang sama tanpa perulangan manual, sehingga penyaringan dan pengambilan sebagian data dapat dirancang dalam satu alur.

Select untuk mengubah bentuk#

Metode Select menerima lambda yang menentukan bentuk baru setiap elemen, sehingga barisan hasilnya berisi nilai turunan dan bukan salinan sumbernya. Proses ini disebut proyeksi, yaitu pemetaan setiap elemen sumber menjadi satu nilai atau objek baru. Contoh berikut memetakan nilai ujian menjadi label kategorinya.

csharp Contoh: Select memetakan nilai menjadi label kategori
// Contoh: Select mengubah setiap nilai menjadi label kategori
string Kategori(int nilai)
{
    return nilai >= 75 ? "lulus" : "remedial";
}

List<int> nilaiUjian = new() { 84, 60, 90 };
foreach (string label in nilaiUjian.Select(n => Kategori(n)))
{
    Console.WriteLine(label);
}
text Keluaran
lulus
remedial
lulus

Jumlah elemen barisan hasil Select selalu sama dengan sumbernya, sedangkan tipenya mengikuti nilai kembalian lambda tersebut. Lambda pada Select juga boleh menghasilkan objek baru, misalnya new { Nama = p.Nama, Status = Kategori(p.Nilai) }, dan bentuk objek tanpa nama seperti itu disebut anonymous type.

Proyeksi bertingkat juga dimungkinkan, misalnya Select yang hasilnya lalu disaring lagi dengan Where pada rantai yang sama. Urutan operator menentukan maknanya, karena menyaring setelah memproyeksikan berarti menilai bentuk barunya, sedangkan menyaring lebih dahulu menilai elemen aslinya. Kaidah praktisnya adalah menyaring terlebih dahulu untuk memperkecil data, kemudian memproyeksikan bentuk akhirnya.

OrderBy untuk mengurutkan#

Metode OrderBy menghasilkan barisan yang elemennya tertata menaik menurut kunci yang dipilih lambda, sedangkan OrderByDescending menghasilkan urutan menurun. Kunci pengurutan boleh berupa bagian data, misalnya nilai, nama, atau tanggal. Metode ThenBy melengkapi urutan utama dengan kunci kedua untuk elemen yang kunci utamanya sama.

csharp Contoh: OrderBy dan OrderByDescending pada nilai ujian
// Contoh: OrderBy dan OrderByDescending menata ulang urutan nilai
List<int> nilaiUjian = new() { 84, 60, 90, 75 };
Console.WriteLine($"Menaik: {string.Join(", ", nilaiUjian.OrderBy(n => n))}");
Console.WriteLine($"Menurun: {string.Join(", ", nilaiUjian.OrderByDescending(n => n))}");
text Keluaran
Menaik: 60, 75, 84, 90
Menurun: 90, 84, 75, 60

Contoh berikut mengurutkan daftar peserta berdasarkan nilai, lalu nama sebagai kunci kedua. Dua peserta dengan nilai 84 akhirnya tampil menurut abjad nama karena peran ThenBy.

Metode OrderBy tidak mengubah collection sumbernya, tetapi menghasilkan barisan baru yang urutannya tertata, sehingga data asli tetap utuh untuk pengolahan lain. Kunci pengurutan boleh berupa hasil perhitungan lambda, misalnya panjang string atau selisih dari nilai acuan, selama tipenya mendukung perbandingan.

csharp Contoh: ThenBy mengurutkan nama untuk nilai yang sama
// Contoh: ThenBy mengurutkan nama untuk nilai yang sama
List<Peserta> peserta = new()
{
    new Peserta("Budi", 84),
    new Peserta("Ali", 84),
    new Peserta("Citra", 90)
};
foreach (Peserta p in peserta.OrderBy(p => p.Nilai).ThenBy(p => p.Nama))
{
    Console.WriteLine($"{p.Nama}: {p.Nilai}");
}

record Peserta(string Nama, int Nilai);
text Keluaran
Ali: 84
Budi: 84
Citra: 90

First dan FirstOrDefault#

Metode First mengembalikan elemen pertama yang cocok dengan predikatnya, sedangkan FirstOrDefault mengembalikan nilai bawaan tipe ketika tidak ada yang cocok. Nilai bawaan untuk tipe angka adalah 0, untuk string adalah null pada konteks nullable yang dinonaktifkan, dan untuk tipe referensi lain adalah null. Perbedaan ini menentukan pilihan, karena First menganggap ketiadaan hasil sebagai kondisi yang tidak wajar.

csharp Contoh: First melempar exception dan FirstOrDefault mengembalikan 0
// Contoh: First melempar exception dan FirstOrDefault mengembalikan nilai bawaan
List<int> nilaiUjian = new() { 84, 60, 90 };
int tertinggi = nilaiUjian.First(n => n >= 80);
Console.WriteLine($"Nilai pertama minimal 80: {tertinggi}");
int tidakAda = nilaiUjian.FirstOrDefault(n => n >= 95);
Console.WriteLine($"Nilai pertama minimal 95: {tidakAda}");

try
{
    int gagal = nilaiUjian.First(n => n >= 95);
    Console.WriteLine($"Nilai pertama minimal 95: {gagal}");
}
catch (InvalidOperationException ex)
{
    Console.WriteLine(ex.Message);
}
text Keluaran
Nilai pertama minimal 80: 84
Nilai pertama minimal 95: 0
Sequence contains no matching element

Pemanggilan First dengan predikat yang tidak dipenuhi elemen mana pun melempar exception InvalidOperationException dengan pesan Sequence contains no matching element. Ketiadaan hasil pada FirstOrDefault tidak berbahaya, tetapi nilai 0 pada contoh di atas bisa juga berarti sebuah nilai ujian yang sah, sehingga kerancuan itu perlu dipastikan sebelum metode ini dipilih. Pasangan serupa juga tersedia, yaitu Single dan SingleOrDefault yang mengharuskan hasilnya paling banyak satu.

Tanpa predikat, First mengembalikan elemen paling depan apa adanya, sedangkan Last dan LastOrDefault mengambil dari ujung sebaliknya. Pada barisan hasil OrderBy, elemen pertama hasil First pasti memuat kunci terkecil, sehingga kombinasi keduanya sering menggantikan perulangan pencarian manual.

Contoh program utuh#

Program berikut merangkum keempat operator pada satu daftar peserta ujian.

csharp Program.cs
// Program contoh Where, Select, OrderBy, serta First dan FirstOrDefault

// Data nilai ujian lima peserta
List<Peserta> peserta = new()
{
    new Peserta("Budi", 84),
    new Peserta("Ali", 60),
    new Peserta("Citra", 90),
    new Peserta("Dewi", 55),
    new Peserta("Eka", 75)
};

// Where: menyaring nilai yang memenuhi ambang lulus
IEnumerable<int> semuaNilai = peserta.Select(p => p.Nilai);
Console.WriteLine($"Nilai yang lulus: {string.Join(", ", semuaNilai.Where(n => n >= 75))}");

// Select: mengubah setiap peserta menjadi baris laporan
foreach (string baris in peserta.Select(p => $"{p.Nama} dinyatakan {Kategori(p.Nilai)}"))
{
    Console.WriteLine(baris);
}

// OrderBy dan ThenBy: nilai menaik, lalu nama menaik untuk nilai yang sama
Console.WriteLine("Urutan berdasarkan nilai lalu nama:");
foreach (Peserta p in peserta.OrderBy(p => p.Nilai).ThenBy(p => p.Nama))
{
    Console.WriteLine($"{p.Nama}: {p.Nilai}");
}

// First: elemen pertama yang cocok, FirstOrDefault: nilai bawaan bila tidak ada
Console.WriteLine($"First minimal 80: {semuaNilai.First(n => n >= 80)}");
Console.WriteLine($"FirstOrDefault minimal 95: {semuaNilai.FirstOrDefault(n => n >= 95)}");
try
{
    Console.WriteLine($"First minimal 95: {semuaNilai.First(n => n >= 95)}");
}
catch (InvalidOperationException ex)
{
    Console.WriteLine(ex.Message);
}

string Kategori(int nilai)
{
    return nilai >= 75 ? "lulus" : "remedial";
}

record Peserta(string Nama, int Nilai);
text Keluaran
Nilai yang lulus: 84, 90, 75
Budi dinyatakan lulus
Ali dinyatakan remedial
Citra dinyatakan lulus
Dewi dinyatakan remedial
Eka dinyatakan lulus
Urutan berdasarkan nilai lalu nama:
Dewi: 55
Ali: 60
Eka: 75
Budi: 84
Citra: 90
First minimal 80: 84
FirstOrDefault minimal 95: 0
Sequence contains no matching element

Jalankan program tersebut dari folder topik dengan perintah berikut.

bash
# aktifkan .NET yang terpasang di sandbox
export DOTNET_ROOT=/home/kal/.dotnet PATH=/home/kal/.dotnet:$PATH DOTNET_CLI_TELEMETRY_OPTOUT=1 DOTNET_NOLOGO=1
# jalankan program contoh dari folder topik
dotnet run --file Program.cs
text Keluaran
Nilai yang lulus: 84, 90, 75
Budi dinyatakan lulus
Ali dinyatakan remedial
Citra dinyatakan lulus
Dewi dinyatakan remedial
Eka dinyatakan lulus
Urutan berdasarkan nilai lalu nama:
Dewi: 55
Ali: 60
Eka: 75
Budi: 84
Citra: 90
First minimal 80: 84
FirstOrDefault minimal 95: 0
Sequence contains no matching element

Kesalahan umum#

  • Memanggil First pada query yang hasilnya mungkin kosong tanpa rencana penanganan. Exception InvalidOperationException terjadi pada runtime, dan perbaikannya adalah menggunakan FirstOrDefault disertai pemeriksaan nilai bawaan.
  • Menganggap 0 dari FirstOrDefault sebagai bukti ketiadaan hasil pada tipe angka. Nilai 0 dapat berupa data yang sah, sehingga pemeriksaan keberadaan hasil terlebih dahulu, misalnya dengan Any(), memberikan kepastian yang lebih baik.
  • Mengurutkan dengan OrderBy dua kali secara berantai. Pemanggilan kedua mengganti urutan pertama, sedangkan kunci tambahan yang dimaksud seharusnya ditulis dengan ThenBy.

Ringkasan#

  • Where menyaring elemen dengan predikat lambda, dan sumbernya tidak pernah berubah.
  • Select memetakan setiap elemen menjadi bentuk baru dengan jumlah yang tetap sama.
  • OrderBy dan OrderByDescending menata urutan menurut kunci, sedangkan ThenBy menambahkan kunci kedua untuk nilai yang seri.
  • First melempar exception ketika tidak ada hasil, sedangkan FirstOrDefault mengembalikan nilai bawaan tipe dan menuntut kehati-hatian terhadap kerancuan nilai itu.

Referensi#