kodingup.camp

Beranda/JavaScript Mastery: Object & Data

Teknik Koleksi Tingkat Lanjut

Modul 12 dari 144 menit bacaAdvanced Collection Techniques

Isi modul

    Kenapa ini penting

    Data nyata sering berbentuk bersarang dan berantakan. Respons API bisa berisi daftar pesanan yang masing-masing punya daftar item. Data tag artikel bisa berisi duplikat. Laporan penjualan perlu dikelompokkan per kategori atau per bulan. Kalau hanya mengandalkan map, filter, dan reduce, kodenya bisa jadi panjang dan sulit dibaca.

    Modul ini menambah perkakas yang membuat pekerjaan semacam itu jauh lebih ringkas: flat dan flatMap untuk meratakan array bersarang, Object.groupBy untuk mengelompokkan data, chaining (merangkai method) untuk membangun "pipa" pengolahan data, Set untuk membuang duplikat, dan destructuring untuk membongkar isi array ke variabel dengan rapi. Teknik-teknik ini sering muncul di kode profesional dan wawancara kerja.

    Konsep inti

    flat: meratakan array bersarang

    arr.flat(kedalaman) membuat array baru dengan sub-array "dibongkar" sampai kedalaman tertentu. Bawaannya kedalaman 1. Pakai flat(Infinity) untuk meratakan sampai habis. Analogi: kamu punya kardus berisi kotak-kotak kecil berisi barang; flat mengeluarkan barang dari kotak kecil dan menjajarkannya langsung di dalam kardus.

    flatMap: map lalu flat satu level

    arr.flatMap(fn) sama dengan arr.map(fn).flat(1), tapi lebih efisien. Callback boleh mengembalikan array (akan diratakan) atau nilai tunggal. Trik berguna: mengembalikan [] artinya "buang elemen ini", sehingga flatMap bisa sekaligus menyaring dan mengubah.

    Object.groupBy: mengelompokkan data

    Object.groupBy(daftar, fn) mengelompokkan elemen berdasarkan kunci yang dikembalikan callback. Hasilnya object dengan setiap kunci berisi array elemen. Ada juga Map.groupBy yang hasilnya Map (berguna kalau kuncinya bukan string, misalnya object atau angka yang ingin dipertahankan tipenya).

    Beberapa hal penting yang perlu kamu tahu:

    Chaining: merangkai method

    Karena map, filter, flatMap, dan toSorted mengembalikan array baru, kamu bisa menyambungnya seperti pipa: data masuk di satu ujung, diolah bertahap, hasil keluar di ujung lain. Tulis satu method per baris supaya mudah dibaca dan mudah dikomentari. Urutan penting untuk efisiensi: lakukan filter sedini mungkin supaya langkah berikutnya memproses lebih sedikit data.

    Dedupe dengan Set

    Set adalah koleksi yang hanya menyimpan nilai unik (dibahas lengkap di modul 14). Trik paling populer: [...new Set(arr)] membuang duplikat dari array nilai primitif. Untuk array berisi object, keunikan dihitung berdasarkan referensi, jadi kamu perlu memilih kunci unik (misalnya id) lalu memakai Map atau filter dengan pelacakan.

    Destructuring array

    Destructuring membongkar elemen array ke variabel berdasarkan posisi:

    Destructuring juga bisa dipakai langsung di parameter function dan di for...of atas Object.entries, seperti yang sudah kamu lihat di modul-modul sebelumnya.

    Contoh kode

    // 1) flat
    const bersarang = [1, [2, 3], [4, [5, [6]]]];
    console.log(bersarang.flat());         // → [ 1, 2, 3, 4, [ 5, [ 6 ] ] ]
    console.log(bersarang.flat(2));        // → [ 1, 2, 3, 4, 5, [ 6 ] ]
    console.log(bersarang.flat(Infinity)); // → [ 1, 2, 3, 4, 5, 6 ]
    
    // 2) flatMap: setiap pesanan punya banyak item
    const pesanan = [
      { id: "P1", items: ["kopi", "roti"] },
      { id: "P2", items: ["teh"] },
      { id: "P3", items: ["kopi", "donat", "teh"] },
    ];
    console.log(pesanan.map((p) => p.items));     // → [ [ 'kopi', 'roti' ], [ 'teh' ], [ 'kopi', 'donat', 'teh' ] ]
    console.log(pesanan.flatMap((p) => p.items)); // → [ 'kopi', 'roti', 'teh', 'kopi', 'donat', 'teh' ]
    
    // flatMap untuk menyaring + mengubah sekaligus: kembalikan [] untuk membuang
    const input = ["12", "abc", "7", ""];
    const angkaValid = input.flatMap((s) => {
      const n = Number(s);
      return s !== "" && !Number.isNaN(n) ? [n] : [];
    });
    console.log(angkaValid); // → [ 12, 7 ]
    
    // 3) Object.groupBy (ES2024)
    console.log(typeof Object.groupBy); // → function
    const penjualan = [
      { produk: "Kaos", kategori: "pakaian", total: 150000 },
      { produk: "Topi", kategori: "aksesoris", total: 90000 },
      { produk: "Jaket", kategori: "pakaian", total: 320000 },
      { produk: "Gelang", kategori: "aksesoris", total: 40000 },
    ];
    const perKategori = Object.groupBy(penjualan, (p) => p.kategori);
    console.log(Object.keys(perKategori));           // → [ 'pakaian', 'aksesoris' ]
    console.log(perKategori.pakaian.map((p) => p.produk)); // → [ 'Kaos', 'Jaket' ]
    console.log(Object.getPrototypeOf(perKategori)); // → null
    console.log(Object.hasOwn(perKategori, "pakaian")); // → true
    
    // Kelompok berdasarkan kondisi
    const perUkuran = Object.groupBy(penjualan, (p) => (p.total >= 100000 ? "besar" : "kecil"));
    console.log(perUkuran.besar.length, perUkuran.kecil.length); // → 2 2
    
    // Cadangan untuk lingkungan lama: reduce
    const perKategoriLama = penjualan.reduce((grup, p) => {
      (grup[p.kategori] ??= []).push(p);
      return grup;
    }, {});
    console.log(perKategoriLama.aksesoris.length); // → 2
    
    // Map.groupBy: kunci tetap bertipe aslinya
    const perTotalGenap = Map.groupBy([1, 2, 3, 4, 5], (n) => n % 2 === 0);
    console.log(perTotalGenap.get(true), perTotalGenap.get(false)); // → [ 2, 4 ] [ 1, 3, 5 ]
    
    // 4) Chaining: total per kategori, diurutkan dari yang terbesar
    const ringkasan = Object.entries(Object.groupBy(penjualan, (p) => p.kategori))
      .map(([kategori, daftar]) => ({
        kategori,
        total: daftar.reduce((jumlah, p) => jumlah + p.total, 0),
      }))
      .toSorted((a, b) => b.total - a.total);
    console.log(ringkasan);
    // → [ { kategori: 'pakaian', total: 470000 }, { kategori: 'aksesoris', total: 130000 } ]
    
    // 5) Dedupe dengan Set
    const semuaItem = pesanan.flatMap((p) => p.items);
    const itemUnik = [...new Set(semuaItem)];
    console.log(itemUnik); // → [ 'kopi', 'roti', 'teh', 'donat' ]
    
    // Dedupe object berdasarkan kunci id (yang terakhir menang)
    const pengguna = [
      { id: 1, nama: "Ana" },
      { id: 2, nama: "Bob" },
      { id: 1, nama: "Ana (update)" },
    ];
    const unikById = [...new Map(pengguna.map((u) => [u.id, u])).values()];
    console.log(unikById); // → [ { id: 1, nama: 'Ana (update)' }, { id: 2, nama: 'Bob' } ]
    
    // 6) Destructuring array
    const skor = [95, 88, 76, 64];
    const [juara1, juara2, ...lainnya] = skor;
    console.log(juara1, juara2, lainnya); // → 95 88 [ 76, 64 ]
    
    const [, , ketiga] = skor;
    console.log(ketiga); // → 76
    
    const [warna = "hitam", ukuran = "M"] = ["putih"];
    console.log(warna, ukuran); // → putih M
    
    let kiri = "A";
    let kanan = "B";
    [kiri, kanan] = [kanan, kiri];
    console.log(kiri, kanan); // → B A
    
    // Destructuring di parameter function
    const luasPersegiPanjang = ([panjang, lebar]) => panjang * lebar;
    console.log(luasPersegiPanjang([8, 5])); // → 40
    

    Kesalahan umum

    1. Menulis arr.groupBy(...). Method itu tidak ada di array. Yang benar Object.groupBy(arr, fn) atau Map.groupBy(arr, fn).
    2. Memakai hasil.hasOwnProperty(...) pada hasil Object.groupBy. Object-nya tanpa prototype, jadi method itu tidak ada dan memanggilnya melempar TypeError. Pakai Object.hasOwn.
    3. Mengira flat() meratakan sampai habis. Bawaannya hanya satu level. Tentukan kedalaman atau pakai Infinity.
    4. Dedupe array object dengan new Set. Dua object berisi sama tetap dianggap berbeda karena referensinya berbeda. Dedupe berdasarkan kunci unik dengan Map.
    5. Rantai method yang terlalu panjang di satu baris. Sulit dibaca dan sulit di-debug. Pisahkan satu method per baris, dan simpan hasil antara ke variabel bernama jelas kalau rantainya mulai rumit.

    Latihan

    1. Mudah, Diberikan const kelas = [["Ani", "Budi"], ["Caca"], ["Dodi", "Eka", "Ani"]]. Buat daftar nama unik dan urutkan secara alfabet.
    2. Sedang, Diberikan array kalimat ["belajar javascript", "javascript itu seru"]. Pakai flatMap untuk menghasilkan daftar semua kata, lalu hitung jumlah kata unik.
    3. Menantang, Diberikan transaksi { tanggal: "2026-10-01", jumlah } dari beberapa bulan. Kelompokkan per bulan (format "2026-10") dengan Object.groupBy, lalu hasilkan array { bulan, total, jumlahTransaksi } yang diurutkan per bulan.
    Lihat solusi
    // 1) Mudah
    const kelas = [["Ani", "Budi"], ["Caca"], ["Dodi", "Eka", "Ani"]];
    const namaUnik = [...new Set(kelas.flat())].toSorted((a, b) => a.localeCompare(b, "id"));
    console.log(namaUnik); // → [ 'Ani', 'Budi', 'Caca', 'Dodi', 'Eka' ]
    
    // 2) Sedang
    const kalimat = ["belajar javascript", "javascript itu seru"];
    const kata = kalimat.flatMap((k) => k.split(" "));
    console.log(kata);                 // → [ 'belajar', 'javascript', 'javascript', 'itu', 'seru' ]
    console.log(new Set(kata).size);   // → 4
    
    // 3) Menantang
    const transaksi = [
      { tanggal: "2026-09-12", jumlah: 200000 },
      { tanggal: "2026-10-01", jumlah: 150000 },
      { tanggal: "2026-09-30", jumlah: 50000 },
      { tanggal: "2026-10-15", jumlah: 300000 },
      { tanggal: "2026-08-05", jumlah: 75000 },
    ];
    const perBulan = Object.entries(Object.groupBy(transaksi, (t) => t.tanggal.slice(0, 7)))
      .map(([bulan, daftar]) => ({
        bulan,
        total: daftar.reduce((s, t) => s + t.jumlah, 0),
        jumlahTransaksi: daftar.length,
      }))
      .toSorted((a, b) => a.bulan.localeCompare(b.bulan));
    console.log(perBulan);
    // → [
    // →   { bulan: '2026-08', total: 75000, jumlahTransaksi: 1 },
    // →   { bulan: '2026-09', total: 250000, jumlahTransaksi: 2 },
    // →   { bulan: '2026-10', total: 450000, jumlahTransaksi: 2 }
    // → ]
    

    Ringkasan

    Kuis pemahaman

    Pilih satu jawaban per soal. Penjelasan muncul setelah kamu menjawab.