Kenapa ini penting
Setelah bisa menyimpan data dalam array, pekerjaan berikutnya adalah mengolahnya: menampilkan semua produk, mengubah harga dalam dolar menjadi rupiah, menyaring produk yang stoknya habis, menghitung total belanja, mencari pengguna berdasarkan email, memeriksa apakah ada pesanan yang belum dibayar, atau mengurutkan produk dari yang termurah.
Semua tugas ini bisa dikerjakan dengan loop for biasa, tapi JavaScript menyediakan method array tingkat tinggi (higher-order methods: method yang menerima function sebagai argumen) yang membuat kode lebih pendek, lebih mudah dibaca, dan lebih sedikit bug. Method seperti map, filter, dan reduce adalah kosakata wajib programmer JavaScript modern. Kamu akan melihatnya di hampir setiap codebase, terutama di React untuk menampilkan daftar.
Konsep inti
Callback
Semua method di modul ini menerima callback: function yang akan dipanggil untuk setiap elemen. Callback menerima tiga argumen: (elemen, indeks, arraynya). Biasanya kamu cukup memakai argumen pertama.
Tabel method utama
| Method | Tujuan | Mengembalikan | Mengubah asli? |
|---|---|---|---|
forEach |
menjalankan aksi per elemen | undefined |
tidak (kecuali callback-nya mengubah) |
map |
mengubah setiap elemen | array baru, panjang sama | tidak |
filter |
menyaring elemen | array baru, bisa lebih pendek | tidak |
reduce |
meringkas jadi satu nilai | nilai apa pun | tidak |
find |
elemen pertama yang cocok | elemen atau undefined |
tidak |
findIndex |
indeks pertama yang cocok | angka atau -1 |
tidak |
findLast / findLastIndex |
sama, dicari dari belakang | elemen/indeks | tidak |
some |
adakah minimal satu yang cocok? | true/false |
tidak |
every |
apakah semua cocok? | true/false |
tidak |
sort |
mengurutkan | array yang sama | ya |
toSorted |
mengurutkan | array baru | tidak |
Analogi dapur: map seperti mesin pengupas yang mengupas setiap kentang (jumlah tetap, bentuk berubah). filter seperti saringan yang hanya meloloskan kentang yang bagus. reduce seperti blender yang menggabungkan semua jadi satu mangkuk sup.
forEach vs map
forEach dipakai untuk efek samping (mencetak, menyimpan ke tempat lain) dan tidak mengembalikan apa-apa. map dipakai untuk menghasilkan array baru. Jangan memakai map kalau kamu tidak memakai hasilnya. Satu hal lagi: forEach tidak bisa dihentikan di tengah dengan break. Kalau butuh berhenti lebih awal, pakai for...of, some, atau find.
reduce
arr.reduce((akumulator, elemen) => ..., nilaiAwal) berjalan dari kiri ke kanan. akumulator adalah "hasil sementara", dan apa pun yang dikembalikan callback menjadi akumulator untuk elemen berikutnya. Selalu beri nilai awal. Tanpa nilai awal, elemen pertama dipakai sebagai akumulator, dan pada array kosong reduce melempar TypeError.
reduce bisa menghasilkan apa saja: angka (total), object (pengelompokan atau hitungan), atau array. Tapi kalau map atau filter sudah cukup, pakai yang lebih sederhana demi keterbacaan.
some dan every
some berhenti begitu menemukan satu yang cocok; every berhenti begitu menemukan satu yang tidak cocok. Pada array kosong, some mengembalikan false dan every mengembalikan true (secara logika, "semua elemen dari kumpulan kosong memenuhi syarat").
Mengurutkan dengan comparator
Tanpa argumen, sort() mengubah elemen menjadi string lalu mengurutkan berdasarkan urutan karakter Unicode. Itu sebabnya [10, 1, 5, 100].sort() menghasilkan [1, 10, 100, 5]. Untuk angka, berikan comparator (function pembanding) (a, b) => ...:
- hasil negatif:
aditaruh sebelumb, - hasil positif:
aditaruh setelahb, - nol: urutan keduanya dipertahankan.
Jadi (a, b) => a - b mengurutkan naik, (a, b) => b - a mengurutkan turun. Untuk teks, pakai a.localeCompare(b, "id").
sort mengubah array asli. Kalau tidak ingin mengubahnya, pakai toSorted (ES2023) atau salin dulu [...arr].sort(...). Sejak ES2019, sort dijamin stabil: elemen yang dianggap sama tetap pada urutan aslinya, sehingga kamu bisa mengurutkan bertingkat dengan aman. Saudaranya, toReversed(), adalah versi tanpa mutasi dari reverse().
Contoh kode
const produk = [
{ nama: "Kaos Polos", kategori: "pakaian", harga: 75000, stok: 12 },
{ nama: "Topi Rajut", kategori: "aksesoris", harga: 45000, stok: 0 },
{ nama: "Jaket Denim", kategori: "pakaian", harga: 320000, stok: 4 },
{ nama: "Tas Selempang", kategori: "aksesoris", harga: 150000, stok: 7 },
{ nama: "Celana Chino", kategori: "pakaian", harga: 210000, stok: 0 },
];
// 1) forEach: efek samping (mencetak)
produk.forEach((p, i) => console.log(`${i + 1}. ${p.nama}`));
// → 1. Kaos Polos
// → 2. Topi Rajut
// → 3. Jaket Denim
// → 4. Tas Selempang
// → 5. Celana Chino
// 2) map: membentuk array baru
const namaSaja = produk.map((p) => p.nama);
console.log(namaSaja.length, namaSaja[2]); // → 5 Jaket Denim
const labelHarga = produk.map((p) => `${p.nama}: Rp${p.harga.toLocaleString("id-ID")}`);
console.log(labelHarga[0]); // → Kaos Polos: Rp75.000
// 3) filter: menyaring
const tersedia = produk.filter((p) => p.stok > 0);
console.log(tersedia.map((p) => p.nama)); // → [ 'Kaos Polos', 'Jaket Denim', 'Tas Selempang' ]
// 4) reduce: meringkas
const nilaiInventaris = produk.reduce((total, p) => total + p.harga * p.stok, 0);
console.log(nilaiInventaris); // → 3230000
// reduce untuk menghitung jumlah per kategori (hasilnya object)
const jumlahPerKategori = produk.reduce((hitung, p) => {
hitung[p.kategori] = (hitung[p.kategori] ?? 0) + 1;
return hitung;
}, {});
console.log(jumlahPerKategori); // → { pakaian: 3, aksesoris: 2 }
try {
[].reduce((a, b) => a + b); // tanpa nilai awal pada array kosong
} catch (err) {
console.log(err.constructor.name); // → TypeError
}
console.log([].reduce((a, b) => a + b, 0)); // → 0 (aman dengan nilai awal)
// 5) find, findIndex, findLast
console.log(produk.find((p) => p.harga > 200000).nama); // → Jaket Denim
console.log(produk.findIndex((p) => p.nama === "Topi Rajut")); // → 1
console.log(produk.find((p) => p.harga > 1000000)); // → undefined
console.log(produk.findLast((p) => p.stok === 0).nama); // → Celana Chino
// 6) some dan every
console.log(produk.some((p) => p.stok === 0)); // → true (ada yang habis)
console.log(produk.every((p) => p.harga > 0)); // → true (semua berharga)
console.log([].some(Boolean), [].every(Boolean)); // → false true
// 7) sort tanpa comparator: jebakan
console.log([10, 1, 5, 100].sort()); // → [ 1, 10, 100, 5 ]
console.log([10, 1, 5, 100].sort((a, b) => a - b)); // → [ 1, 5, 10, 100 ]
// toSorted: urutkan tanpa mengubah asli
const termurah = produk.toSorted((a, b) => a.harga - b.harga);
console.log(termurah.map((p) => p.harga).join(", ")); // → 45000, 75000, 150000, 210000, 320000
console.log(produk[0].nama); // → Kaos Polos (urutan asli tetap)
// Urutkan teks sesuai bahasa Indonesia
const namaUrut = produk.map((p) => p.nama).toSorted((a, b) => a.localeCompare(b, "id"));
console.log(namaUrut[0], "|", namaUrut.at(-1)); // → Celana Chino | Topi Rajut
// Urut bertingkat: kategori naik, lalu harga turun
const bertingkat = produk.toSorted(
(a, b) => a.kategori.localeCompare(b.kategori) || b.harga - a.harga
);
console.log(bertingkat.map((p) => p.nama).join(", "));
// → Tas Selempang, Topi Rajut, Jaket Denim, Celana Chino, Kaos Polos
// 8) Mengombinasikan: total harga produk pakaian yang tersedia
const totalPakaian = produk
.filter((p) => p.kategori === "pakaian" && p.stok > 0)
.map((p) => p.harga)
.reduce((a, b) => a + b, 0);
console.log(totalPakaian); // → 395000
Kesalahan umum
- Lupa
returndi callback dengan kurung kurawal.arr.map((x) => { x * 2 })menghasilkan array berisiundefined. Tambahkanreturn, atau pakai bentuk ringkas(x) => x * 2. - Memakai
sort()tanpa comparator untuk angka. Hasilnya urutan teks. Selalu tulis(a, b) => a - b. - Tidak sadar
sortmengubah array asli. Data asli (misalnya dari state) ikut teracak. PakaitoSortedatau[...arr].sort(...). reducetanpa nilai awal. RawanTypeErrorpada array kosong dan hasil aneh pada array berisi object. Selalu beri nilai awal seperti0,{}, atau[].- Memakai
filter(...)[0]untuk mencari satu elemen.filtermemeriksa semua elemen;findberhenti di elemen pertama yang cocok dan lebih jelas maksudnya.
Latihan
- Mudah, Diberikan
const nilai = [55, 80, 72, 90, 64]. Buat array baru berisi nilai yang ditambah 5 poin, lalu saring yang di atas 75. - Sedang, Diberikan array transaksi
{ jenis: "masuk" | "keluar", jumlah }. Hitung saldo akhir denganreduce(masuk menambah, keluar mengurangi) dan cek dengansomeapakah ada transaksi keluar di atas 1.000.000. - Menantang, Diberikan array siswa
{ nama, kelas, nilai }. Buat daftar peringkat: urutkan berdasarkan nilai tertinggi; jika nilai sama, urutkan nama secara alfabet. Tampilkan sebagai teks"1. Nama (nilai)"tanpa mengubah array asli.
Lihat solusi
// 1) Mudah
const nilai = [55, 80, 72, 90, 64];
const hasil = nilai.map((n) => n + 5).filter((n) => n > 75);
console.log(hasil); // → [ 85, 77, 95 ]
// 2) Sedang
const transaksi = [
{ jenis: "masuk", jumlah: 5000000 },
{ jenis: "keluar", jumlah: 1250000 },
{ jenis: "keluar", jumlah: 300000 },
{ jenis: "masuk", jumlah: 750000 },
];
const saldo = transaksi.reduce((s, t) => (t.jenis === "masuk" ? s + t.jumlah : s - t.jumlah), 0);
console.log(saldo); // → 4200000
console.log(transaksi.some((t) => t.jenis === "keluar" && t.jumlah > 1000000)); // → true
// 3) Menantang
const siswa = [
{ nama: "Citra", kelas: "8A", nilai: 88 },
{ nama: "Bayu", kelas: "8A", nilai: 92 },
{ nama: "Andi", kelas: "8B", nilai: 88 },
{ nama: "Dewi", kelas: "8B", nilai: 75 },
];
const peringkat = siswa
.toSorted((a, b) => b.nilai - a.nilai || a.nama.localeCompare(b.nama, "id"))
.map((s, i) => `${i + 1}. ${s.nama} (${s.nilai})`);
console.log(peringkat);
// → [ '1. Bayu (92)', '2. Andi (88)', '3. Citra (88)', '4. Dewi (75)' ]
console.log(siswa[0].nama); // → Citra (asli tidak berubah)
Ringkasan
forEachuntuk efek samping,mapuntuk mengubah setiap elemen,filteruntuk menyaring.reducemeringkas array menjadi satu nilai apa pun; selalu beri nilai awal.find/findIndexmencari elemen pertama yang cocok,some/everymemeriksa kondisi dan berhenti lebih awal.sort()tanpa comparator mengurutkan sebagai teks; pakai(a, b) => a - buntuk angka danlocaleCompareuntuk teks.sortmengubah array asli,toSortedmengembalikan array baru.