Masukkan Password
76 Cineflix: Filter Berdasarkan Genre
Lecture ini melanjutkan fitur Search sebelumnya, tetapi sekarang kita membuat Filter berdasarkan Genre.
Kalau Search sebelumnya menjawab:
“Movie mana yang namanya mengandung kata yang dicari?”
Maka Filter Genre menjawab:
“Movie mana yang memiliki genre tertentu?”
Dan yang paling penting dari lecture ini adalah kamu mulai melihat bahwa filter() + includes() bisa digunakan bukan hanya untuk String, tetapi juga untuk Array.
1. Gambaran fitur yang akan dibuat
Di aplikasi ada pilihan:
Filter by Genre
(●) All
( ) Action
( ) Sci-fi
( ) Drama
( ) Comedy
Misalnya user memilih:
(●) Comedy
Maka hanya movie yang mempunyai genre "comedy" yang ditampilkan.
Misalnya data:
const movies = [
{
name: "Movie A",
genre: ["action", "drama"]
},
{
name: "Movie B",
genre: ["comedy"]
},
{
name: "Movie C",
genre: ["action", "sci-fi", "drama"]
}
];
Kalau pilih:
Comedy
hasil:
Movie B
Kalau pilih:
Drama
hasil:
Movie A
Movie C
Kalau pilih:
Action
hasil:
Movie A
Movie C
Dan kalau pilih:
All
semua movie kembali ditampilkan.
2. Konsep besar lecture
Flow-nya:
User memilih genre
↓
event click
↓
filterByGenre()
↓
ambil value radio button
↓
selectedGenre
↓
apakah "all"?
↙ ↘
YES NO
↓ ↓
semua movie filter()
│ ↓
│ movie.genre
│ ↓
│ includes()
│ ↓
│ true/false
│ ↓
│ filteredMovies
└──────┬───┘
↓
renderAllMovies()
↓
UI
3. Kenapa menggunakan radio button?
Di HTML kita mempunyai beberapa radio button:
<input
type="radio"
name="genre"
value="all"
>
<input
type="radio"
name="genre"
value="action"
>
<input
type="radio"
name="genre"
value="sci-fi"
>
<input
type="radio"
name="genre"
value="drama"
>
<input
type="radio"
name="genre"
value="comedy"
>
Radio button cocok karena user hanya boleh memilih satu genre pada satu waktu.
Contohnya:
( ) All
( ) Action
(●) Drama
( ) Comedy
Tidak bisa:
(●) Action
(●) Drama
secara bersamaan jika mereka berada dalam name group yang sama.
4. Apa fungsi value?
Perhatikan:
<input
type="radio"
value="drama"
>
value adalah nilai yang ingin kita ambil dari radio button.
Misalnya user memilih Drama:
selectedRadioButton.value
hasilnya:
"drama"
Kalau memilih Comedy:
"comedy"
Kalau memilih Sci-fi:
"sci-fi"
Jadi:
Radio button
↓
value
↓
"drama"
5. this pada inline event handler
Lecture menggunakan:
onclick="filterByGenre(this)"
Ini sangat penting karena kamu sudah belajar tentang this.
Di sini this menunjuk ke element HTML yang sedang menjalankan event handler tersebut.
Misalnya:
<input
type="radio"
value="drama"
onclick="filterByGenre(this)"
>
Ketika radio button tersebut diklik:
this
↓
radio button Drama
Kemudian dikirim ke function:
filterByGenre(this);
6. Function menerima radio button
Function-nya:
function filterByGenre(selectedRadioButton) {
}
Jadi:
this
↓
radio button
↓
selectedRadioButton
Misalnya user memilih Drama:
selectedRadioButton
↓
<input type="radio" value="drama">
Maka:
selectedRadioButton.value
menghasilkan:
"drama"
7. Kenapa parameter dinamakan selectedRadioButton?
Karena isi parameter tersebut bukan genre langsung.
Ini:
selectedRadioButton
adalah element radio button.
Sedangkan:
selectedRadioButton.value
adalah nilai genre-nya.
Jadi bedakan:
selectedRadioButton
→ DOM element
selectedRadioButton.value
→ String genre
Contoh:
selectedRadioButton
↓
<input type="radio" value="comedy">
selectedRadioButton.value
↓
"comedy"
8. Mengambil genre yang dipilih
Di dalam function:
function filterByGenre(selectedRadioButton) {
let selectedGenre = selectedRadioButton.value;
}
Misalnya user memilih:
Drama
maka:
selectedGenre
berisi:
"drama"
Kalau memilih:
Sci-fi
maka:
selectedGenre
berisi:
"sci-fi"
9. Kita membuat filteredMovies
Kemudian lecture membuat array kosong:
let filteredMovies = [];
Kenapa array kosong?
Karena nanti kita akan mengisinya dengan hasil filter.
Awalnya:
filteredMovies
↓
[]
Kalau memilih Comedy:
filteredMovies
↓
[Movie B]
Kalau memilih Drama:
filteredMovies
↓
[Movie A, Movie C, Movie D]
10. Kenapa filteredMovies menggunakan let, bukan const?
Ini merupakan error yang muncul di lecture.
Awalnya mungkin:
const filteredMovies = [];
Kemudian:
filteredMovies = movies;
JavaScript akan memberikan error:
Assignment to constant variable
Karena const tidak boleh di-assign ulang.
Contoh:
const x = 10;
x = 20; // Error
Begitu juga:
const filteredMovies = [];
filteredMovies = movies; // Error
Karena kita ingin mengganti isi binding filteredMovies dari:
[]
menjadi:
movies
maka gunakan:
let filteredMovies = [];
11. Ini mengingatkan kita pada let vs const
Ingat:
const
→ tidak boleh reassignment
let
→ boleh reassignment
Di sini:
let filteredMovies = [];
karena nanti:
filteredMovies = movies;
atau:
filteredMovies = movies.filter(...);
Jadi let memang diperlukan.
12. Sekarang cek apakah genre yang dipilih adalah all
Kita mempunyai:
if (selectedGenre === "all") {
filteredMovies = movies;
}
Kenapa?
Karena All bukan genre sebenarnya.
all berarti:
“Jangan filter berdasarkan genre. Tampilkan semuanya.”
Jadi kalau:
selectedGenre === "all"
langsung:
filteredMovies = movies;
Hasilnya:
filteredMovies
↓
movies
↓
semua movie
13. Kenapa ===, bukan ==?
Karena kita sudah belajar bahwa modern JavaScript lebih dianjurkan menggunakan strict equality:
selectedGenre === "all"
Artinya:
Nilainya harus sama dan tipe datanya juga sama.
Misalnya:
"all" === "all"
→ true
Sedangkan:
"all" === "drama"
→ false
14. Kalau bukan all, kita gunakan filter()
Sekarang bagian utama:
else {
filteredMovies = movies.filter((movie) => {
});
}
Artinya:
“Kalau user memilih genre tertentu, pilih movie yang mempunyai genre tersebut.”
15. Struktur data movie penting untuk dipahami
Movie kita mempunyai:
{
name: "Interstellar",
genre: ["sci-fi", "drama"]
}
Perhatikan:
movie.genre
bukan String.
Tapi Array:
["sci-fi", "drama"]
Ini berbeda dengan lecture Search.
Search sebelumnya
Kita menggunakan:
movie.name
yang merupakan String:
"Interstellar"
Maka:
movie.name.includes(searchText)
Filter sekarang
Kita menggunakan:
movie.genre
yang merupakan Array:
["sci-fi", "drama"]
Maka:
movie.genre.includes(selectedGenre)
16. Ini salah satu konsep terpenting lecture
includes() bisa digunakan pada:
String
"Interstellar".includes("star")
→ true
Array
["action", "drama"].includes("drama")
→ true
Jadi:
includes()
↓
bisa mencari sesuatu
di dalam String atau Array
17. Bagaimana includes() pada Array bekerja?
Misalnya:
let genres = ["action", "sci-fi", "drama"];
Kita bisa:
genres.includes("action");
hasil:
true
Karena "action" ada di dalam array.
Kemudian:
genres.includes("comedy");
hasil:
false
Karena "comedy" tidak ada.
18. Jadi filter movie-nya sangat sederhana
Kita tulis:
filteredMovies = movies.filter((movie) => {
return movie.genre.includes(selectedGenre);
});
Baca seperti manusia:
“Dari semua movie, pilih movie yang array
genre-nya mengandungselectedGenre.”
19. Contoh ketika memilih drama
Misalnya:
selectedGenre = "drama";
Movies:
[
{
name: "Movie A",
genre: ["action", "drama"]
},
{
name: "Movie B",
genre: ["comedy"]
},
{
name: "Movie C",
genre: ["sci-fi", "drama"]
}
]
filter() mengecek satu per satu.
Movie A
["action", "drama"].includes("drama")
→ true
Maka:
Movie A → masuk
Movie B
["comedy"].includes("drama")
→ false
Maka:
Movie B → tidak masuk
Movie C
["sci-fi", "drama"].includes("drama")
→ true
Maka:
Movie C → masuk
Hasil:
[
Movie A,
Movie C
]
20. Visual proses filter()
movies
│
├── Movie A
│ genre: ["action", "drama"]
│ ↓
│ includes("drama")
│ ↓
│ true
│ ↓
│ MASUK
│
├── Movie B
│ genre: ["comedy"]
│ ↓
│ includes("drama")
│ ↓
│ false
│ ↓
│ ABAIKAN
│
└── Movie C
genre: ["sci-fi", "drama"]
↓
includes("drama")
↓
true
↓
MASUK
Hasil:
filteredMovies
↓
[Movie A, Movie C]
21. Perbedaan Search dan Filter Genre
Ini sangat penting karena kedua lecture menggunakan:
filter()
dan:
includes()
Tetapi objek yang diperiksa berbeda.
Search Movie
movies.filter((movie) => {
return movie.name
.toLowerCase()
.includes(searchText.toLowerCase());
});
Kita mencari dalam:
String
↓
movie.name
Contoh:
"Interstellar"
Filter Genre
movies.filter((movie) => {
return movie.genre.includes(selectedGenre);
});
Kita mencari dalam:
Array
↓
movie.genre
Contoh:
["action", "drama"]
22. Jadi includes() menyesuaikan tipe datanya
Ini mental model yang bagus:
String.includes()
→ apakah teks ini ada di dalam String?
Array.includes()
→ apakah element ini ada di dalam Array?
Contoh String:
"hello world".includes("world");
→ true
Contoh Array:
["action", "drama"].includes("drama");
→ true
23. Setelah filter selesai, render ulang
Setelah:
filteredMovies = movies.filter(...);
kita mendapatkan array baru.
Misalnya:
filteredMovies
↓
[Movie A, Movie C]
Kemudian:
renderAllMovies(filteredMovies);
Artinya:
“Tampilkan hanya movie yang ada di hasil filter.”
24. Ingat fungsi renderAllMovies()
Sebelumnya kita sudah membuat:
function renderAllMovies(movieList) {
movieListSection.innerHTML = "";
movieList.forEach((movie) => {
// buat movie card
});
}
Jadi ketika:
renderAllMovies(filteredMovies);
dipanggil:
hapus movie cards lama
↓
loop filteredMovies
↓
buat card
↓
tampilkan
25. Full function filterByGenre()
Kurang lebih:
function filterByGenre(selectedRadioButton) {
let selectedGenre = selectedRadioButton.value;
let filteredMovies = [];
if (selectedGenre === "all") {
filteredMovies = movies;
} else {
filteredMovies = movies.filter((movie) => {
return movie.genre.includes(selectedGenre);
});
}
renderAllMovies(filteredMovies);
}
Ini sebenarnya function yang cukup sederhana kalau dibaca langkah demi langkah.
26. Mari baca function tersebut dengan bahasa manusia
function filterByGenre(selectedRadioButton) {
“Buat function untuk melakukan filter genre. Function menerima radio button yang dipilih.”
let selectedGenre = selectedRadioButton.value;
“Ambil genre dari radio button.”
Misalnya:
"drama"
let filteredMovies = [];
“Siapkan tempat untuk menyimpan hasil filter.”
if (selectedGenre === "all") {
“Kalau user memilih All…”
filteredMovies = movies;
“…gunakan semua movie.”
else {
“Kalau bukan All…”
filteredMovies = movies.filter((movie) => {
“…filter semua movie.”
return movie.genre.includes(selectedGenre);
“Movie hanya boleh masuk kalau genre-nya mengandung genre yang dipilih.”
renderAllMovies(filteredMovies);
“Tampilkan hasilnya di browser.”
27. Flow ketika memilih Drama
Misalnya user klik:
(●) Drama
HTML menjalankan:
filterByGenre(this);
Kemudian:
this
↓
radio button Drama
↓
selectedRadioButton
↓
selectedRadioButton.value
↓
"drama"
Kemudian:
"drama"
↓
movies.filter()
↓
periksa movie satu per satu
↓
movie.genre.includes("drama")
↓
true / false
↓
filteredMovies
↓
renderAllMovies(filteredMovies)
↓
UI
28. Bagaimana dengan All?
Ketika:
(●) All
maka:
selectedGenre === "all"
adalah:
true
Jadi kita tidak perlu menjalankan filter.
Langsung:
filteredMovies = movies;
Kemudian:
renderAllMovies(filteredMovies);
Karena:
filteredMovies === movies
secara referensi pada kasus assignment ini, semua movie akan dirender.
29. Kenapa All perlu perlakuan khusus?
Karena:
movie.genre.includes("all")
hampir pasti:
false
Tidak ada movie yang memiliki genre:
"all"
Jadi kalau kita memperlakukan all sebagai genre biasa:
movies.filter((movie) => {
return movie.genre.includes("all");
});
hasilnya kemungkinan:
[]
Padahal All artinya:
tampilkan semua.
Maka kita perlu:
if (selectedGenre === "all") {
filteredMovies = movies;
}
30. Perbedaan All dengan genre lainnya
selectedGenre
↓
┌──────────────────────┐
│ "all" ? │
└──────────────────────┘
↓ ↓
YES NO
↓ ↓
semua movie filter()
↓
genre.includes(...)
Ini pola yang sangat umum:
Ada satu pilihan khusus yang berarti “jangan filter”.
31. Mengapa filteredMovies = movies tidak berarti membuat copy?
Ini catatan teknis kecil.
Ketika:
filteredMovies = movies;
keduanya menunjuk ke array yang sama:
filteredMovies ─┐
↓
movies array
↑
movies ─────────┘
Tetapi dalam function ini kita hanya akan melakukan:
renderAllMovies(filteredMovies);
dan tidak memodifikasinya.
Jadi aman untuk kebutuhan lecture ini.
Kalau ingin membuat array baru:
filteredMovies = [...movies];
Tetapi belum diperlukan untuk memahami lecture.
32. filter() menghasilkan array baru
Pada bagian:
filteredMovies = movies.filter(...);
filter() membuat array hasil baru.
Misalnya:
movies
↓
[A, B, C, D]
filter()
↓
[B, D]
filteredMovies
↓
[B, D]
Array movies tetap:
[A, B, C, D]
Ini penting untuk project karena filter genre seharusnya tidak menghapus movie dari data utama.
33. Ini berbeda dengan sort()
Kita sudah belajar lecture sebelumnya:
movies.sort(...)
mengubah movies.
Sedangkan:
movies.filter(...)
tidak mengubah movies.
Jadi:
SORT
↓
mengubah urutan array asli
FILTER
↓
membuat array hasil baru
34. Search dan Genre Filter ternyata sangat mirip
Kalau kamu perhatikan dua lecture terakhir:
Search
input user
↓
searchText
↓
movies.filter()
↓
movie.name
↓
includes(searchText)
↓
searchedMovies
↓
render
Genre Filter
radio button
↓
selectedGenre
↓
movies.filter()
↓
movie.genre
↓
includes(selectedGenre)
↓
filteredMovies
↓
render
Strukturnya hampir identik!
Yang berubah hanya:
SEARCH
→ movie.name
GENRE FILTER
→ movie.genre
35. Ini menunjukkan kekuatan filter()
filter() tidak peduli kita sedang melakukan:
- search
- filter genre
- filter rating
- filter year
- filter harga
- filter status
Yang penting kita memberikan kondisi:
array.filter((item) => {
return CONDITION;
});
Misalnya:
movies.filter((movie) => movie.rating > 4);
atau:
movies.filter((movie) => movie.releaseYear > 2010);
atau:
movies.filter((movie) => movie.genre.includes("drama"));
36. Perhatikan tipe data movie.genre
Ini salah satu alasan mengapa kita harus memahami data structure.
Movie:
{
name: "Interstellar",
genre: ["sci-fi", "drama"]
}
genre adalah:
Array
bukan:
String
Jadi kita menggunakan:
movie.genre.includes(...)
Kalau misalnya genre disimpan sebagai:
genre: "sci-fi"
barulah kita menggunakan String includes().
37. Satu movie bisa punya banyak genre
Ini alasan data genre dibuat sebagai Array.
Contoh:
genre: ["action", "sci-fi", "drama"]
Movie tersebut bisa lolos untuk tiga filter:
Action → masuk
Sci-fi → masuk
Drama → masuk
Comedy → tidak masuk
Jadi:
movie.genre.includes(selectedGenre)
sangat cocok.
38. Contoh lengkap
Misalnya:
const movies = [
{
name: "Interstellar",
genre: ["sci-fi", "drama"]
},
{
name: "Bad Boys",
genre: ["action", "comedy"]
},
{
name: "The Dark Knight",
genre: ["action", "drama"]
},
{
name: "Movie X",
genre: ["comedy"]
}
];
Pilih action
Bad Boys
The Dark Knight
Pilih drama
Interstellar
The Dark Knight
Pilih sci-fi
Interstellar
Pilih comedy
Bad Boys
Movie X
Pilih all
Interstellar
Bad Boys
The Dark Knight
Movie X
39. Hubungkan dengan konsep Boolean
Kita sudah belajar bahwa:
includes()
menghasilkan:
true / false
Sedangkan:
filter()
membutuhkan kondisi yang bisa menghasilkan:
true / false
Jadi:
movie.genre.includes(selectedGenre)
adalah pasangan yang sangat cocok untuk filter().
Visual:
movie.genre
↓
["action", "drama"]
↓
includes("drama")
↓
true
↓
filter()
↓
movie masuk
40. Kenapa tidak menggunakan forEach()?
Kita sebenarnya bisa saja menggunakan loop manual:
let filteredMovies = [];
movies.forEach((movie) => {
if (movie.genre.includes(selectedGenre)) {
filteredMovies.push(movie);
}
});
Ini bisa bekerja.
Tetapi filter() jauh lebih cocok:
let filteredMovies = movies.filter((movie) => {
return movie.genre.includes(selectedGenre);
});
Karena tujuan kita memang:
memilih sebagian elemen dari array.
Jadi:
ingin memilih → filter()
41. Bandingkan kedua cara
Cara manual
let filteredMovies = [];
movies.forEach((movie) => {
if (movie.genre.includes(selectedGenre)) {
filteredMovies.push(movie);
}
});
Dengan filter()
let filteredMovies = movies.filter((movie) => {
return movie.genre.includes(selectedGenre);
});
Cara kedua lebih singkat dan lebih jelas maksudnya.
42. Hubungan dengan lecture Search sebelumnya
Search:
const searchedMovies = movies.filter((movie) => {
return movie.name
.toLowerCase()
.includes(searchText.toLowerCase());
});
Genre:
const filteredMovies = movies.filter((movie) => {
return movie.genre.includes(selectedGenre);
});
Perhatikan struktur:
movies.filter()
↓
movie
↓
kondisi
↓
true / false
Yang berbeda hanya kondisinya.
43. Ada satu hal yang harus diperhatikan: case sensitivity
Data genre:
["action", "drama", "sci-fi"]
dan:
selectedGenre = "Drama";
Kalau:
movie.genre.includes(selectedGenre)
maka:
["action", "drama"].includes("Drama")
hasilnya:
false
Karena:
"drama" ≠ "Drama"
Untungnya di project, value radio button dan data genre dibuat dengan format lowercase:
value="drama"
dan:
genre: ["drama"]
Jadi cocok.
44. Jangan tertukar value dan checked
Ini juga menghubungkan dengan lecture form sebelumnya.
Untuk radio button:
selectedRadioButton.value
digunakan untuk mendapatkan:
nilai genre
Sedangkan:
selectedRadioButton.checked
digunakan untuk mengetahui:
apakah radio button dipilih?
Contoh:
value → "drama"
checked → true
Dalam lecture ini kita tidak perlu mengecek .checked, karena kita sudah mendapatkan radio button yang diklik melalui:
this
45. Full flow dari HTML sampai UI
HTML
─────────────────────────
<input value="action">
<input value="sci-fi">
<input value="drama">
<input value="comedy">
<input value="all">
↓ click
filterByGenre(this)
↓
JavaScript
─────────────────────────
selectedRadioButton.value
↓
selectedGenre = "drama"
↓
movies.filter()
↓
movie.genre.includes("drama")
↓
true / false
↓
filteredMovies
↓
renderAllMovies(filteredMovies)
↓
DOM
↓
Browser UI
46. Bagian paling penting untuk dihafal
Kalau kamu ingin mengambil inti lecture ini, hafalkan pola:
const filteredMovies = movies.filter((movie) => {
return movie.genre.includes(selectedGenre);
});
Baca:
“Dari semua movie, ambil movie yang genre-nya mengandung genre yang dipilih user.”
Dan untuk All:
if (selectedGenre === "all") {
filteredMovies = movies;
}
Baca:
“Kalau user memilih All, jangan filter; tampilkan semuanya.”
47. Cheat Sheet
| Konsep | Fungsi |
|---|---|
onclick="filterByGenre(this)" | kirim radio button yang diklik |
this | radio button yang sedang diklik |
.value | mengambil nilai genre |
selectedGenre | genre yang dipilih |
filter() | memilih movie yang cocok |
movie.genre | Array genre sebuah movie |
includes() | mengecek apakah genre ada dalam Array |
true | movie masuk hasil |
false | movie tidak masuk |
filteredMovies | array hasil filter |
renderAllMovies() | menampilkan hasil ke UI |
innerHTML = "" | membersihkan UI lama |
48. Perbandingan tiga lecture terakhir
Sekarang kamu sudah melihat tiga operasi penting:
movies
│
┌───────────┼───────────┐
↓ ↓ ↓
SORT SEARCH FILTER
↓ ↓ ↓
sort() filter() filter()
↓ ↓ ↓
urutkan movie.name movie.genre
↓ ↓ ↓
ASC/ includes() includes()
DESC ↓ ↓
↓ ↓
hasil[] hasil[]
└───────────┼───────────┘
↓
renderAllMovies()
↓
UI
Jadi sekarang mental model kamu bisa dibuat:
sort()
→ URUTKAN
filter()
→ PILIH
includes()
→ CEK APAKAH ADA
renderAllMovies()
→ TAMPILKAN
49. Satu perbedaan yang sangat penting
Perhatikan:
sort()
movies.sort(...)
mengubah:
movies asli
filter()
movies.filter(...)
menghasilkan:
array baru
Jadi:
SORT
movies
↓
ubah urutan movies
FILTER
movies
↓
buat filteredMovies
Ini akan sangat penting nanti ketika fitur-fitur project mulai digabungkan.
50. 🧠 Mental model terakhir
Bayangkan movies adalah rak semua film.
movies
┌────────────────────┐
│ Interstellar │
│ Bad Boys │
│ Dark Knight │
│ Bahubali │
│ Inception │
└────────────────────┘
Ketika user memilih Drama, filter() bertindak seperti petugas yang bertanya kepada setiap movie:
“Apakah kamu punya genre Drama?”
Movie 1:
["sci-fi", "drama"]
→ YA
→ masuk
Movie 2:
["action", "comedy"]
→ TIDAK
→ abaikan
Movie 3:
["action", "drama"]
→ YA
→ masuk
Akhirnya:
filteredMovies
┌────────────────────┐
│ Interstellar │
│ Dark Knight │
└────────────────────┘
Lalu:
renderAllMovies(filteredMovies);
mengatakan:
“Sekarang tampilkan hanya film yang lolos.”
Jadi inti lecture ini sebenarnya sangat sederhana:
USER MEMILIH GENRE
↓
AMBIL VALUE
↓
movies.filter()
↓
movie.genre.includes(selectedGenre)
↓
true = masuk
false = tidak
↓
filteredMovies
↓
renderAllMovies()
↓
UI
Dan pola yang perlu benar-benar kamu kuasai adalah:
filter()memilih data, sedangkanincludes()menentukan apakah data tersebut memenuhi syarat.
Itulah mengapa kombinasi:
movies.filter((movie) => {
return movie.genre.includes(selectedGenre);
});
menjadi inti dari fitur Filter by Genre.