Metode sort mengurutkan elemen array dan menjadi bagian akhir dari hampir semua pipeline pengolahan data, yaitu rangkaian transformasi yang dijalankan satu tahap demi satu tahap. Topik ini membahas perilaku sort yang mengubah array asalnya beserta dua cara aman menghindarinya, penulisan fungsi pembanding untuk angka dan string, pengurutan object berdasarkan kunci, serta pemanggilan metode yang tersambung dari map, filter, dan sort. Pembahasan ditutup dengan satu pipeline rekap nilai ujian yang lengkap dan aturan kapan rantai sebaiknya dipecah menjadi konstanta per langkah.
sort memutasi array asal#
Metode sort mengurutkan elemen array di tempat, artinya array asalnya ikut berubah, dan nilai baliknya menunjuk array yang sama. Perilaku ini berbeda dari map dan filter yang selalu membuat array baru, sehingga kebiasaan menampung hasil sort saja tidak cukup untuk melindungi data aslinya.
const suhu = [29, 24, 31, 26];
const terurut = suhu.sort((a, b) => a - b);
console.log(suhu);
console.log(terurut);
console.log(suhu === terurut);[ 24, 26, 29, 31 ]
[ 24, 26, 29, 31 ]
trueHasil suhu === terurut berupa true karena kedua variabel memegang array yang sama persis, hanya isiannya yang sudah dipindah. Perubahan terjadi langsung pada array yang sama, bukan pada salinan terpisah. Urutan asli [29, 24, 31, 26] hilang dan tidak bisa dikembalikan kecuali data itu dimuat ulang dari sumbernya.
Dua jalur aman tersedia untuk kasus ini. Salinan spread [...suhu] membuat array baru berisi elemen yang sama, lalu sort memutasi array baru itu. Metode toSorted menangani penyalinan itu sendiri, sehingga satu panggilan sudah cukup.
const suhu = [29, 24, 31, 26];
const salinan = [...suhu].sort((a, b) => a - b);
const tanpaMutasi = suhu.toSorted((a, b) => a - b);
console.log(suhu);
console.log(salinan);
console.log(tanpaMutasi);[ 29, 24, 31, 26 ]
[ 24, 26, 29, 31 ]
[ 24, 26, 29, 31 ]Array suhu tetap dalam urutan aslinya setelah kedua operasi, sementara salinan dan tanpaMutasi memegang array baru yang sudah terurut. Hasil keduanya identik, sehingga pilihannya mengikuti dukungan lingkungan kerja.
Comparator angka dan string#
Fungsi pembanding yang dioper ke sort disebut comparator, yakni fungsi yang menerima dua elemen dan mengembalikan angka negatif, nol, atau positif untuk menentukan urutannya. Nilai negatif menempatkan elemen pertama sebelum elemen kedua, nilai positif sebaliknya, dan nol berarti keduanya setara. Tanpa comparator, sort mengonversi setiap elemen menjadi string dan mengurutkannya secara alfabet, perilaku bawaan yang sering menghasilkan urutan keliru untuk angka.
const stok = [3, 100, 25, 7];
console.log(stok.sort());
console.log(stok.sort((a, b) => a - b));[ 100, 25, 3, 7 ]
[ 3, 7, 25, 100 ]Bentuk tanpa comparator menempatkan "100" sebelum "25" karena huruf 1 mendahului 2 secara alfabet. Ekspresi (a, b) => a - b mengurutkan menaik, sedangkan b - a mengurutkan menurun, dan keduanya bisa dihitung ulang dengan mudah dari selisih kedua angkanya. Angka negatif memajukan a, angka positif memundurkannya, dan nol membiarkan posisinya tetap.
Untuk teks, comparator memakai localeCompare, metode string yang membandingkan dua kata menurut kaidah locale, yaitu aturan pengurutan yang berlaku bagi suatu bahasa. Perbedaannya terlihat jelas pada campuran huruf besar dan kecil.
const buku = ["Panduan Tanaman", "Atlas Laut", "kamus kecil", "Bahasa Tubuh"];
console.log(buku.toSorted());
console.log(buku.toSorted((a, b) => a.localeCompare(b)));[ 'Atlas Laut', 'Bahasa Tubuh', 'Panduan Tanaman', 'kamus kecil' ]
[ 'Atlas Laut', 'Bahasa Tubuh', 'kamus kecil', 'Panduan Tanaman' ]Sort bawaan menempatkan semua judul berawalan huruf besar lebih dahulu karena urutan kode karakternya, sehingga kamus kecil jatuh di akhir. Dengan localeCompare, huruf besar dan kecil diperlakukan setara dalam penimbangan utama, sehingga kamus kecil berada di urutan alfabet yang wajar.
Mengurutkan object berdasarkan kunci#
Pengurutan array object dilakukan dengan comparator yang membaca satu properti sebagai kuncinya. Bentuknya tetap selisih untuk angka dan localeCompare untuk teks, hanya sumber nilainya berpindah ke properti masing-masing object. Data nilai ujian berikut diurutkan menurun menurut nilai, lalu menaik menurut nama.
const ujian = [
{ nama: "Ali", nilai: 78 },
{ nama: "Budi", nilai: 92 },
{ nama: "Citra", nilai: 65 },
{ nama: "Dewi", nilai: 84 },
];
const peringkat = ujian.toSorted((a, b) => b.nilai - a.nilai);
const alfabet = ujian.toSorted((a, b) => a.nama.localeCompare(b.nama));
console.log(peringkat.map((s) => `${s.nama} ${s.nilai}`).join("\n"));
console.log(alfabet.map((s) => s.nama).join(", "));Budi 92
Dewi 84
Ali 78
Citra 65
Ali, Budi, Citra, DewiEkspresi b.nilai - a.nilai membalik selisihnya sehingga nilai terbesar maju ke depan, bentuk standar untuk papan peringkat. Kedua urutan berasal dari array yang sama dan keduanya memakai toSorted, sehingga array ujian tetap dalam urutan aslinya. Bila dua data ternyata setara menurut kuncinya, comparator yang mengembalikan nol membiarkan urutannya mengikuti keadaan sebelumnya, jadi tambahkan kunci cadangan bila urutan setara perlu stabil.
Method chaining dan keterbacaannya#
Karena map, filter, dan toSorted sama-sama mengembalikan array baru, pemanggilannya dapat disambung, sebuah gaya yang disebut method chaining, yakni pemanggilan metode berikutnya langsung pada nilai balik metode sebelumnya. Setiap tahap menerima array dari tahap di atasnya dan menyerahkan array baru ke tahap di bawahnya. Ketiga metode itu memang dirancang untuk pola semacam ini karena tidak ada yang memutasi array sumbernya.
const nilai = [78, 92, 65, 84, 71, 90];
const bonus = nilai
.filter((n) => n >= 75)
.map((n) => n + 5)
.toSorted((a, b) => b - a);
console.log(bonus);[ 97, 95, 89, 83 ]Rantai di atas menyaring empat nilai yang lulus, menambahkan bonus 5 poin pada masing-masing, lalu mengurutkannya menurun. Menulis setiap metode pada barisnya sendiri membuat tahapan terbaca dari atas ke bawah seperti daftar langkah, dan pemformatan inilah yang lazim untuk rantai lebih dari satu metode. Kelemahan rantainya adalah nilai antar tahap tidak punya nama, sehingga kekeliruan pada satu tahap harus ditelusuri dari seluruh rantainya.
Pipeline rekap nilai ujian#
Pipeline menggabungkan seluruh bekal topik ini menjadi satu alur kerja, dari data mentah sampai rekap yang siap ditampilkan. Diagram berikut menggambarkan lima tahapnya beserta jumlah data yang tersisa pada setiap tahap. Setiap tahap menerima array dari tahap sebelumnya dan menyerahkan array baru ke tahap berikutnya.
Berkas berikut menerapkan alur tersebut. Data mentah berupa lima object siswa, difilter untuk siswa yang lulus, dipetakan menjadi baris rekap berpredikat, diurutkan menurun, lalu disambung menjadi satu teks.
// Pipeline kecil: dari nilai mentah sampai rekap terurut
const mentah = [
{ nama: "Ali", nilai: 78 },
{ nama: "Budi", nilai: 92 },
{ nama: "Citra", nilai: 65 },
{ nama: "Dewi", nilai: 84 },
{ nama: "Eka", nilai: 71 },
];
const lulus = mentah.filter((s) => s.nilai >= 75);
const berpredikat = lulus.map((s) => ({
nama: s.nama,
nilai: s.nilai,
predikat: s.nilai >= 85 ? "sangat baik" : "baik",
}));
const terurut = berpredikat.toSorted((a, b) => b.nilai - a.nilai);
const baris = terurut.map((s) => `${s.nama}: ${s.nilai} (${s.predikat})`);
console.log(baris.join("\n"));Budi: 92 (sangat baik)
Dewi: 84 (baik)
Ali: 78 (baik)# jalankan berkas contoh dari direktori topik
node pipeline-rekap.jsBudi: 92 (sangat baik)
Dewi: 84 (baik)
Ali: 78 (baik)Keluaran blok bash sama persis karena perintahnya menjalankan berkas yang sama. Dari lima siswa, tiga yang lolos batas 75, dan ambang 85 memisahkan predikat “sangat baik” dari “baik”. Tahap terakhir memakai map untuk pemformatan, bukan sort, karena urutannya sudah selesai dan tugas yang tersisa hanya menyusun teks per baris.
Kapan rantai dipecah menjadi konstanta#
Rantai yang pendek dan jelas layak dipertahankan, sedangkan rantai yang memaksakan terlalu banyak tanggung jawab lebih baik dipecah menjadi konstanta per tahap. Tidak ada batas keras jumlah metodenya, yang dinilai adalah apakah tugas setiap tahap masih bisa disebut dengan satu frasa yang jelas. Kriteria berikut menjadi penanda yang lazim dipakai.
- Satu tahap butuh komentar panjang agar bisa dijelaskan, padahal nama konstanta yang tepat sudah cukup untuk itu.
- Nilai antara perlu diperiksa saat debugging, misalnya untuk melihat daftar yang sudah difilter sebelum dipetakan.
- Rantai memanjang lebih dari sekitar empat tahap atau mencampur transformasi data dengan pemformatan tampilan.
- Hasil salah satu tahap dibutuhkan lagi di tempat lain, sehingga menampungnya menghindari pekerjaan ganda.
Bentuk pecahan dari pipeline bonus pada bagian sebelumnya terlihat seperti ini, dan setiap nilai antaranya kini bisa dicetak.
const mentah = [78, 92, 65, 84, 71, 90];
const lulus = mentah.filter((n) => n >= 75);
const berbonus = lulus.map((n) => n + 5);
const terurut = berbonus.toSorted((a, b) => b - a);
console.log(lulus);
console.log(berbonus);
console.log(terurut);[ 78, 92, 84, 90 ]
[ 83, 97, 89, 95 ]
[ 97, 95, 89, 83 ]Kode di atas mengerjakan hal yang sama dengan rantai filter, map, toSorted sebelumnya dan menghasilkan array yang persis sama. Perbedaannya ada pada pengamatan, karena setiap tahap kini punya nama yang bisa dicetak. Tiga baris console.log tambahan dapat dipasang sementara lalu dihapus setelah hasilnya diyakini benar.
Ringkasan#
sortmemutasi array asal dan nilai baliknya menunjuk array yang sama, sedangkan salinan spread dantoSortedmenghasilkan array baru.- Sort bawaan mengurutkan sebagai string, sehingga angka butuh comparator
(a, b) => a - bdan teks butuhlocaleCompare. - Object diurutkan melalui propertinya, misalnya
b.nilai - a.nilaiuntuk urutan menurun pada kunci nilai. - Method chaining menyusun pipeline per tahap, dan rantai yang panjang, butuh debugging, atau mencampur tugas dipecah menjadi konstanta per langkah.
Referensi#
- Array.prototype.sort() — MDN — dokumentasi sort beserta perilaku mutasi dan urutan bawaan berbasis string.
- Array.prototype.toSorted() — MDN — dokumentasi varian sort yang mengembalikan array baru tanpa menyentuh sumbernya.
- String.prototype.localeCompare() — MDN — dokumentasi perbandingan string yang mengikuti kaidah bahasa setempat.