reduce

Akumulasi array menjadi satu nilai dan kapan perulangan biasa lebih jelas.

Daftar isi

Metode reduce melipat seluruh isi array menjadi satu nilai akhir, misalnya jumlah angka, object rekap penghitungan, atau satu string gabungan. Fungsi pemrosesnya dijalankan berulang kali sambil membawa satu penampung yang terus diperbarui, dan nilai akhirnya adalah hasil penampung setelah elemen terakhir selesai diproses. Topik ini membahas bentuk dasar akumulasinya, peran nilai awal beserta error yang muncul tanpanya, pola object penghitung, dan batas kapan perulangan biasa menyatakan maksud dengan lebih jelas.

Akumulasi menjadi satu nilai#

Callback reduce menerima dua parameter utama, yaitu akumulator, yakni variabel penampung yang isinya dibawa dari satu elemen ke elemen berikutnya, dan elemen yang sedang diproses. Nilai balik callback menjadi isi akumulator untuk putaran berikutnya, sedangkan argumen kedua pada reduce menetapkan isi awal akumulator tersebut. Setelah semua elemen habis, isi akumulator terakhir dikembalikan sebagai hasil akhirnya, berupa satu nilai dan bukan array.

Contoh klasiknya adalah menjumlahkan nilai ujian. Nilai awal 0 membuat penjumlahan tetap benar apa pun isinya, dan tabel di bawahnya menelusuri setiap langkahnya.

js Contoh: menjumlahkan nilai dengan reduce
const nilai = [78, 92, 65, 84];
const total = nilai.reduce((total, n) => total + n, 0);

console.log(total);
text Keluaran
319
Langkahtotal sebelumelementotal sesudah
nilai awal(belum ada)(belum ada)0
107878
27892170
317065235
423584319

Tabel itu memperlihatkan inti reduce, setiap langkah hanya mengerjakan satu elemen sambil memperbarui penampungnya. Hasilnya 319, sebuah angka tunggal yang tidak punya indeks dan tidak bisa ditelusuri mundur ke elemen asalnya. Bila salah satu nilai berubah, hanya langkah yang menyentuhnya ikut berubah, dan sisa langkahnya tetap mudah dihitung ulang.

Nilai awal dan array kosong#

Nilai awal menentukan titik mulai akumulasi sekaligus perilaku reduce pada array kosong. Bila nilai awal diberikan, array kosong langsung mengembalikan nilai itu tanpa memanggil callback satu kali pun. Bila nilai awal tidak diberikan, reduce memakai elemen pertama sebagai akumulator dan mulai memproses dari elemen kedua. Pada array kosong, langkah itu tidak mungkin dilakukan sehingga muncul error. Errornya dapat diperagakan dengan aman menggunakan try/catch, sehingga program tetap berjalan dan pesannya tertangkap.

js Contoh: reduce tanpa nilai awal pada array kosong
const kosong = [];

try {
  const total = kosong.reduce((total, n) => total + n);
  console.log(`total: ${total}`);
} catch (error) {
  console.log(`Error tertangkap: ${error.message}`);
}
text Keluaran
Error tertangkap: Reduce of empty array with no initial value
js Contoh: nilai awal menyelamatkan array kosong
const kosong = [];
const total = kosong.reduce((total, n) => total + n, 0);

console.log(total);
text Keluaran
0

Pada array yang tidak kosong, bentuk tanpa nilai awal tetap bekerja untuk penjumlahan biasa, misalnya [78, 92, 65] menghasilkan 235 karena penjumlahan angka tidak peduli urutan mulainya. Bentuk ini rawan bila bentuk akumulasinya lebih rumit, karena elemen pertama dipaksa menjadi akumulator tanpa pernah melewati callback. Karena itu bentuk satu baris seperti ini terasa ringkas pada penjumlahan, tetapi menyembunyikan langkah pentingnya dari pembaca.

Penghitung dan gabungan string#

Bentuk akumulasi tidak harus angka, akumulator bisa berupa object yang diisi sambil jalan. Pola penghitung memakai nilai awal {}, lalu setiap elemen menaikkan hitungannya sendiri. Berkas berikut merapikan frekuensi kata pada satu kalimat.

js penghitung-kata.js
// Rekap frekuensi kata pada satu kalimat
const kalimat = "kopi teh kopi susu teh kopi";
const hitungan = kalimat.split(" ").reduce((rekap, kata) => {
  rekap[kata] = (rekap[kata] ?? 0) + 1;
  return rekap;
}, {});

console.log(hitungan);
text Keluaran
{ kopi: 3, teh: 2, susu: 1 }
bash
# jalankan berkas contoh dari direktori topik
node penghitung-kata.js
text Keluaran
{ kopi: 3, teh: 2, susu: 1 }

Keluarannya sama karena perintah bash menjalankan berkas yang sama. Ekspresi rekap[kata] ?? 0 mengisi nilai 0 untuk kata yang belum pernah muncul, sehingga penambahannya tidak pernah melibatkan undefined. Akumulator berupa object ini juga menegaskan pentingnya nilai awal, karena tanpa {} elemen pertama yang berupa string akan dianggap sebagai rekap.

Akumulasi teks memanfaatkan posisi elemen untuk mengatur pemisahnya. Gabungan seperti “A, B dan C” tidak bisa dibuat dengan join(", ") biasa, karena kata terakhir butuh penyambung yang berbeda. Pembedanya diperiksa lewat parameter indeks yang diterima callback.

js Contoh: daftar nama dengan penyambung terakhir
const nama = ["Ali", "Budi", "Citra", "Dewi"];
const daftar = nama.reduce((teks, n, i) => {
  if (i === 0) return n;
  if (i === nama.length - 1) return `${teks} dan ${n}`;
  return `${teks}, ${n}`;
}, "");

console.log(daftar);
text Keluaran
Ali, Budi, Citra dan Dewi

Ekspresi i === 0 menangkap elemen pertama tanpa penyambung, sedangkan i === nama.length - 1 menyambungkan kata terakhir dengan “dan”. Sisa elemen memakai koma seperti biasa. Pola ini berguna untuk kalimat ringkasan, misalnya daftar nama peserta yang tampil dalam satu baris.

Kapan perulangan biasa lebih jelas#

Kerapatan reduce membuat pembaca harus mensimulasikan akumulatornya di kepala untuk menebak hasilnya. Bila tugasnya bukan melipat data menjadi nilai baru yang bentuknya bergantung pada seluruh elemen, perulangan biasa sering menyatakan maksud dengan lebih langsung. Contohnya mencari data nilai tertinggi, tugas yang tujuannya jelas dari namanya saja.

js Contoh: tugas yang sama, dua gaya
const ujian = [
  { nama: "Ali", nilai: 78 },
  { nama: "Budi", nilai: 92 },
  { nama: "Citra", nilai: 65 },
  { nama: "Dewi", nilai: 84 },
];

// reduce yang dipaksakan mencari nilai tertinggi
const viaReduce = ujian.reduce((terbaik, s) =>
  s.nilai > terbaik.nilai ? s : terbaik
);

// perulangan biasa untuk tugas yang sama
let viaLoop = ujian[0];
for (const s of ujian) {
  if (s.nilai > viaLoop.nilai) {
    viaLoop = s;
  }
}

console.log(viaReduce);
console.log(viaLoop);
text Keluaran
{ nama: 'Budi', nilai: 92 }
{ nama: 'Budi', nilai: 92 }

Kedua versi mengembalikan data yang sama, tetapi versi perulangan menyimpan keadaannya dalam variabel bernama yang bisa dicetak kapan saja untuk diperiksa. Aturan praktisnya, reduce dipakai bila hasil akhirnya memang bentukan dari seluruh elemen, seperti jumlah, rekap, atau gabungan. Sebaliknya, pencarian, pemilahan dua kelompok, dan penghentian lebih awal semuanya lebih pas ditulis sebagai perulangan. Memilih bentuk yang paling mudah dibaca adalah bagian dari pekerjaan, bukan sekadar gaya penulisan.

Kesalahan umum#

  1. Lupa return rekap di dalam badan callback. Callback yang memakai kurung kurawal wajib mengembalikan akumulatornya, karena nilai balik itulah yang dipakai pada putaran berikutnya. Tanpa return, akumulator berikutnya bernilai undefined dan langkah kedua melempar error Cannot read properties of undefined. Periksa setiap cabang if di dalam badannya, karena satu jalur yang lupa mengembalikan nilai sudah cukup untuk merusak seluruh rekapnya.
  2. Mengisi nilai awal dengan bentuk yang salah. Nilai awal 0 untuk rekap object membuat hasilnya diam-diam keliru, karena menuliskan properti ke angka tidak menyimpan apa pun dan reduce tetap mengembalikan angka itu. Samakan bentuk nilai awal dengan bentuk hasil akhirnya, {} untuk object dan "" untuk teks.
  3. Memakai reduce untuk mencetak atau efek samping. Bila tidak ada nilai untuk dikumpulkan menjadi satu, reduce hanya menambah lapisan yang membingungkan tanpa memperpendek kode. Perulangan biasa atau metode yang tepat menyatakan maksud dengan lebih baik, dan penampung sementaranya bisa dicetak kapan saja saat diperiksa.

Ringkasan#

  • reduce melipat array menjadi satu nilai dengan akumulator yang diperbarui callback pada setiap elemen, dan argumen kedua menetapkan isi awalnya.
  • Tanpa nilai awal, array kosong melempar Reduce of empty array with no initial value, sedangkan dengan nilai awal, array kosong mengembalikan nilai itu apa adanya.
  • Bentuk akumulasi bebas, dari angka untuk jumlah, object untuk rekap penghitungan, sampai string untuk gabungan berpola.
  • Bila bentuk hasilnya sulit ditebak tanpa menelusuri putaran, perulangan biasa menyatakan maksud yang sama dengan lebih jelas.

Referensi#