Masukkan Password
71 Cineflix: Format Deskripsi Film
Lecture ini melanjutkan project admin panel streaming service sebelumnya. Kali ini ada dua pekerjaan utama:
- Memotong deskripsi film supaya tidak terlalu panjang.
- Merapikan genre supaya setiap genre diawali huruf kapital dan dipisahkan dengan koma + spasi.
Yang menarik, lecture ini menggabungkan banyak method yang sudah kita pelajari sebelumnya:
substring()
concat()
at()
toUpperCase()
toLowerCase()
slice()
flatMap()
1. Gambaran masalahnya
Sebelumnya setiap movie menampilkan description secara lengkap.
Misalnya:
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua...
Kalau description sangat panjang, card movie akan menjadi terlalu tinggi.
Yang diinginkan:
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor...
Kemudian:
...
Jika user ingin membaca lengkap, user bisa klik tombol Details.
Selain itu, genre awalnya mungkin:
action,sci-fi,drama
Yang diinginkan:
Action, Sci-fi, Drama
2. Bagian pertama: format movie description
Kita membuat function:
function formatMovieDescription(desc) {
...
}
Function ini menerima:
desc = description movie
Misalnya:
formatMovieDescription(
"Lorem ipsum dolor sit amet..."
);
Tujuan function:
description panjang
↓
ambil 100 karakter pertama
↓
tambahkan "..."
↓
description pendek
3. Menggunakan substring()
Lecture menggunakan:
desc.substring(0, 100)
Mari pahami artinya.
Misalnya:
let text = "Hello World";
Kemudian:
text.substring(0, 5);
hasil:
Hello
Kenapa?
Karena index-nya:
H e l l o W o r l d
0 1 2 3 4 5 6 7 8 9...
substring(0, 5)
berarti:
mulai dari index
0, berhenti sebelum index5.
Jadi yang diambil:
index 0 → H
index 1 → e
index 2 → l
index 3 → l
index 4 → o
Hasil:
Hello
4. substring(0, 100) dalam project
Kalau:
desc.substring(0, 100)
artinya:
Ambil description mulai dari karakter index 0 sampai sebelum index 100.
Secara sederhana kita bisa menyebutnya:
ambil maksimal 100 karakter pertama.
Misalnya description:
ABCDEFGHIJKLMNOPQRSTUVWXYZ...
maka:
desc.substring(0, 100)
mengambil bagian awalnya.
5. Kemudian tambahkan ...
Setelah mendapatkan 100 karakter:
desc.substring(0, 100)
kita ingin menghasilkan:
100 karakter...
Lecture menggunakan:
.concat("...")
Jadi:
desc.substring(0, 100).concat("...")
Perhatikan di sini ada method chaining.
Urutannya:
desc
↓
substring(0, 100)
↓
String pendek
↓
concat("...")
↓
String pendek + ...
6. Contoh sederhana
Misalnya:
let desc = "Hello World, this is JavaScript";
Kemudian:
desc.substring(0, 11)
hasil:
Hello World
Kemudian:
desc.substring(0, 11).concat("...")
hasil:
Hello World...
7. Function lengkapnya
Secara konsep:
function formatMovieDescription(desc) {
return desc.substring(0, 100).concat("...");
}
Kalau:
formatMovieDescription(
"Lorem ipsum dolor sit amet..."
);
hasilnya kira-kira:
Lorem ipsum dolor sit amet...
8. Kenapa concat() bisa dipanggil setelah substring()?
Ini bagian yang bagus untuk memahami method chaining.
Perhatikan:
desc.substring(0, 100).concat("...")
JavaScript membacanya seperti:
desc
↓
substring(0, 100)
↓
menghasilkan String
↓
concat("...")
↓
menghasilkan String baru
Jadi sebenarnya:
let shortDescription = desc.substring(0, 100);
let finalDescription = shortDescription.concat("...");
Sama konsepnya.
Hanya saja ditulis lebih ringkas:
desc.substring(0, 100).concat("...")
9. Kemudian function dipanggil saat membuat card
Sebelumnya mungkin:
${movie.description}
Sekarang:
${formatMovieDescription(movie.description)}
Artinya:
movie.description
↓
formatMovieDescription()
↓
substring(0, 100)
↓
concat("...")
↓
description pendek
↓
masukkan ke HTML
Jadi data asli movie tidak perlu kita potong.
Kita hanya membuat versi tampilan yang lebih pendek.
10. Apakah movie.description berubah?
Tidak.
Misalnya:
movie.description
tetap berisi description lengkap.
Function:
formatMovieDescription(movie.description)
hanya menghasilkan String baru.
Ini sesuai konsep yang sudah kita pelajari:
String bersifat immutable.
Jadi:
let text = "Hello World";
let shortText = text.substring(0, 5);
tidak membuat:
text
berubah menjadi "Hello".
Tetap:
text = "Hello World"
shortText = "Hello"
11. Bagian kedua: formatting genre
Sekarang masalah berikutnya.
Di movie object:
{
genre: ["action", "sci-fi", "drama"]
}
Yang tampil:
action,sci-fi,drama
Kita ingin:
Action, Sci-fi, Drama
Ada dua pekerjaan:
- Huruf pertama setiap genre → uppercase.
- Sisa karakter → lowercase.
Kemudian kita juga ingin:
Action, Sci-fi, Drama
↑
spasi
setelah koma.
12. Perhatikan bahwa genre adalah Array
Ini penting.
movie.genre bukan String.
Misalnya:
movie.genre
isinya:
["action", "sci-fi", "drama"]
Jadi kita harus memproses:
action
sci-fi
drama
satu per satu.
Ini mirip sekali dengan lecture sebelumnya tentang formatting movie name.
13. Kita membuat formatGenre()
Function:
function formatGenre(genres) {
...
}
Parameter:
genres
berisi Array.
Contoh:
genres = ["action", "sci-fi", "drama"];
14. Menggunakan flatMap()
Lecture menggunakan:
genres.flatMap((genre) => {
...
});
Nah, ini bagian yang agak lebih sulit.
Sebelumnya kita sudah belajar map().
map():
Array
↓
map()
↓
Array baru
flatMap() secara konsep:
Array
↓
map()
↓
flat()
↓
Array yang diratakan
Jadi:
flatMap()adalah kombinasimap()+flat()dalam satu langkah.
15. Kenapa lecture menggunakan flatMap()?
Untuk kasus sederhana seperti:
["action", "sci-fi", "drama"]
sebenarnya map() saja sudah cukup.
Contohnya:
genres.map((genre) => {
return formatGenre;
});
Tetapi lecture sedang sekaligus mengenalkan kembali flatMap() yang sudah dipelajari.
Jadi jangan berpikir:
“Untuk formatting genre harus pakai flatMap.”
Tidak harus.
Untuk transformasi satu genre menjadi satu String, map() sebenarnya lebih natural.
16. Sekarang proses satu genre
Misalnya:
genre = "aCTION"
Kita ingin:
Action
Caranya sama seperti lecture sebelumnya.
Pertama:
genre.at(0)
mengambil huruf pertama:
"a"
Kemudian:
genre.at(0).toUpperCase()
menjadi:
"A"
17. Ambil sisa karakter
Kemudian:
genre.slice(1)
mengambil mulai index 1 sampai akhir.
Misalnya:
"aCTION"
index:
0 1 2 3 4 5
↓ ↓ ↓ ↓ ↓ ↓
a C T I O N
└─────────┘
CTION
Maka:
genre.slice(1)
→
"CTION"
Kemudian:
.toLowerCase()
menjadi:
"ction"
18. Gabungkan
Sekarang kita punya:
"A"
dan:
"ction"
Gabungkan:
"A" + "ction"
hasil:
"Action"
Jadi satu genre berhasil diformat.
19. Alur satu genre
Misalnya:
"aCTION"
menjadi:
"aCTION"
│
├── at(0)
│ ↓
│ "a"
│ ↓
│ toUpperCase()
│ ↓
│ "A"
│
└── slice(1)
↓
"CTION"
↓
toLowerCase()
↓
"ction"
"A" + "ction"
↓
"Action"
Ini hampir sama persis dengan formatting movie name sebelumnya.
20. Sekarang proses seluruh Array
Misalnya:
genres = ["action", "sci-fi", "drama"];
flatMap() menjalankan callback untuk setiap element.
Iterasi 1
genre = "action"
→
"Action"
Iterasi 2
genre = "sci-fi"
→
"Sci-fi"
Iterasi 3
genre = "drama"
→
"Drama"
Hasilnya:
["Action", "Sci-fi", "Drama"]
21. Tapi ada satu detail: spasi setelah koma
Kalau Array tersebut dimasukkan langsung ke template literal:
`${["Action", "Sci-fi", "Drama"]}`
JavaScript biasanya mengubah Array menjadi String seperti:
Action,Sci-fi,Drama
Perhatikan:
Action,Sci-fi,Drama
↑
tidak ada spasi
Lecture ingin:
Action, Sci-fi, Drama
22. Kenapa bisa muncul koma?
Ketika Array dikonversi menjadi String secara otomatis, secara default element-nya dipisahkan dengan koma.
Contoh:
let genres = ["Action", "Sci-fi", "Drama"];
console.log(genres.toString());
hasil:
Action,Sci-fi,Drama
Ini mirip dengan:
genres.join(",");
23. Cara yang lebih jelas sebenarnya join(", ")
Untuk kebutuhan ini, cara yang sangat natural adalah:
formattedGenres.join(", ")
Hasil:
Action, Sci-fi, Drama
Karena separator-nya:
", "
yaitu:
koma + spasi
24. Lecture melakukan spasi di tempat lain
Lecture tampaknya mengembalikan String yang sudah diawali spasi:
return " " + formattedGenre;
atau konsep serupa.
Tujuannya supaya hasilnya memiliki:
Action, Sci-fi, Drama
↑
spasi
Namun secara desain, kalau memang tujuan kita adalah mengubah Array menjadi String yang dipisahkan koma + spasi, join(", ") biasanya lebih mudah dipahami.
Misalnya:
let formattedGenres = genres.map((genre) => {
let firstChar = genre.at(0).toUpperCase();
let restChar = genre.slice(1).toLowerCase();
return firstChar + restChar;
});
return formattedGenres.join(", ");
Ini lebih eksplisit.
25. map() vs flatMap() untuk kasus ini
Mari kita bedakan.
Dengan map()
let formattedGenres = genres.map((genre) => {
return formattedGenre;
});
Hasil:
["Action", "Sci-fi", "Drama"]
Lalu:
formattedGenres.join(", ");
menjadi:
Action, Sci-fi, Drama
Sangat jelas.
Dengan flatMap()
genres.flatMap((genre) => {
return formattedGenre;
});
Karena setiap callback mengembalikan String, hasilnya juga:
["Action", "Sci-fi", "Drama"]
flatMap() sebenarnya melakukan flatten, tetapi tidak terlalu memberikan manfaat tambahan di sini.
Jadi pahami:
Lecture menggunakan
flatMap()untuk mempraktikkan method tersebut, tetapi problem formatting genre sendiri tidak mengharuskanflatMap().
26. Kenapa disebut flatMap()?
Bayangkan:
let numbers = [1, 2, 3];
Kalau:
numbers.map((number) => [number, number]);
hasilnya:
[
[1, 1],
[2, 2],
[3, 3]
]
Ada Array di dalam Array.
Kemudian flat():
[
[1, 1],
[2, 2],
[3, 3]
].flat();
menjadi:
[1, 1, 2, 2, 3, 3]
flatMap() menggabungkan dua langkah tersebut:
numbers.flatMap((number) => [number, number]);
hasil:
[1, 1, 2, 2, 3, 3]
Jadi:
map() → transform
flat() → ratakan
flatMap() → transform + ratakan
27. Tapi dalam kasus genre?
Genre:
["action", "sci-fi", "drama"]
setiap genre:
"action"
berubah menjadi:
"Action"
Satu input → satu output.
Tidak ada nested Array.
Jadi:
map()
sudah cukup.
28. Hubungan dengan lecture sebelumnya
Ini yang bagus dari project ini.
Pada lecture sebelumnya kita punya:
formatMovieName()
yang bekerja pada:
String
Sekarang:
formatGenre()
bekerja pada:
Array of String
Tetapi konsep dasarnya hampir sama.
Movie name
"bad boys"
↓
split()
↓
["bad", "boys"]
↓
map()
↓
["Bad", "Boys"]
↓
join()
↓
"Bad Boys"
Genre
["action", "sci-fi", "drama"]
↓
map/flatMap
↓
["Action", "Sci-fi", "Drama"]
↓
join(", ")
↓
"Action, Sci-fi, Drama"
Bedanya:
Movie name awalnya String, sedangkan genre dari awal sudah Array.
29. Kesalahan yang terjadi di lecture
Ada error:
Cannot read properties of undefined
reading 'flatMap'
Lalu ternyata kode yang digunakan salah.
Lecture kemudian memperbaikinya menjadi:
formatGenre(movie.genre)
Ini error yang sangat umum ketika bekerja dengan object.
Misalnya object:
let movie = {
name: "Interstellar",
genre: ["action", "sci-fi"]
};
Yang benar:
movie.genre
Yang salah misalnya:
movie.genres
karena property-nya tidak ada.
Hasil:
movie.genres
adalah:
undefined
Kemudian kalau kita mencoba:
movie.genres.flatMap(...)
JavaScript berkata:
“Saya tidak bisa menjalankan
flatMap()padaundefined.”
30. Kenapa error-nya mengatakan undefined?
Misalnya:
let x = undefined;
x.flatMap(...)
Mustahil.
Karena:
undefined
bukan Array.
flatMap() adalah method Array.
Jadi error:
Cannot read properties of undefined
biasanya membuat kita perlu bertanya:
“Kenapa variable yang seharusnya berisi Array ternyata
undefined?”
Dalam kasus lecture:
movie.genre
adalah Array.
Jadi harus dipastikan property yang diberikan memang benar.
31. Alur lengkap formatGenre()
Misalnya:
movie.genre = ["aCTION", "SCI-FI", "drama"];
Function melakukan:
["aCTION", "SCI-FI", "drama"]
│
↓
map/flatMap
│
┌──────────────┼──────────────┐
↓ ↓ ↓
"aCTION" "SCI-FI" "drama"
↓ ↓ ↓
"Action" "Sci-fi" "Drama"
└──────────────┼──────────────┘
↓
["Action", "Sci-fi", "Drama"]
│
↓
join(", ")
│
↓
"Action, Sci-fi, Drama"
32. Perbandingan dua function yang kita buat
Sekarang project mempunyai dua formatter.
formatMovieName()
Input:
String
Output:
String
Contoh:
"bAD boys"
↓
"Bad Boys"
formatMovieDescription()
Input:
String
Output:
String
Contoh:
"Lorem ipsum .... [panjang]"
↓
"Lorem ipsum ......."
formatGenre()
Input:
Array
Output:
String
Contoh:
["action", "sci-fi"]
↓
"Action, Sci-fi"
Jadi:
| Function | Input | Output |
|---|---|---|
formatMovieName() | String | String |
formatMovieDescription() | String | String |
formatGenre() | Array | String |
33. Kenapa formatter ini bagus dibuat terpisah?
Bayangkan kode rendering movie card:
movies.forEach((movie) => {
// HTML movie card
});
Di dalam template literal kita bisa menulis:
${formatMovieName(movie.name)}
${formatMovieDescription(movie.description)}
${formatGenre(movie.genre)}
Ini membuat kode lebih mudah dibaca.
Daripada memasukkan semua logic panjang ke dalam HTML template.
Secara mental:
movie
│
├── name
│ ↓
│ formatMovieName()
│
├── description
│ ↓
│ formatMovieDescription()
│
└── genre
↓
formatGenre()
Setiap function mempunyai tugas spesifik.
34. Connection dengan substring() yang sudah dipelajari
Sebelumnya:
"Hello World".substring(0, 5)
Sekarang digunakan untuk kebutuhan nyata:
desc.substring(0, 100)
Jadi kita tidak lagi sekadar belajar:
“substring itu method untuk mengambil sebagian String.”
Sekarang kita tahu use case-nya:
“substring bisa digunakan untuk membuat preview/teaser dari text panjang.”
Ini bisa dipakai untuk:
- deskripsi movie
- artikel
- berita
- product description
- komentar
- preview postingan
Misalnya:
Judul artikel
Lorem ipsum dolor sit amet, consectetur...
35. Connection dengan concat()
Sebelumnya:
"Hello".concat(" World")
Sekarang:
desc.substring(0, 100).concat("...")
Jadi concat() digunakan untuk menambahkan:
...
ke hasil potongan description.
36. Connection dengan at()
Sebelumnya kita belajar:
"Hello".at(0)
Sekarang digunakan untuk:
genre.at(0).toUpperCase()
Jadi:
genre
↓
at(0)
↓
first character
↓
toUpperCase()
37. Connection dengan slice()
Untuk mengambil sisa karakter:
genre.slice(1)
Misalnya:
"action"
menjadi:
"ction"
Kemudian:
.toLowerCase()
menjadi:
"ction"
38. Connection dengan map() / flatMap()
Sebelumnya kita belajar secara teori:
map()
→ transform array
Sekarang kita melakukan:
["action", "drama", "comedy"]
↓
map()
↓
["Action", "Drama", "Comedy"]
Ini contoh penggunaan map() yang benar-benar realistis.
39. Connection dengan join()
Setelah transformasi:
["Action", "Drama", "Comedy"]
kita ingin menampilkan sebagai text:
Action, Drama, Comedy
Maka:
.join(", ")
Jadi:
Array
↓
join(", ")
↓
String
40. Mental model paling penting dari lecture ini
Ada dua pola.
Pattern 1 — Description
String panjang
↓
substring(0, 100)
↓
String pendek
↓
concat("...")
↓
Preview
Pattern 2 — Genre
Array genre
↓
map/flatMap
↓
format setiap genre
↓
Array genre terformat
↓
join(", ")
↓
String genre
41. Cheat sheet
| Method | Kegunaan dalam project |
|---|---|
substring(0, 100) | mengambil 100 karakter pertama |
concat("...") | menambahkan ... |
at(0) | mengambil huruf pertama |
toUpperCase() | membuat huruf pertama kapital |
slice(1) | mengambil sisa karakter |
toLowerCase() | membuat sisa karakter lowercase |
map() | memformat setiap genre |
flatMap() | transform + flatten |
join(", ") | menggabungkan genre dengan koma + spasi |
42. Satu hal yang perlu kamu waspadai
Ada perbedaan antara:
substring(0, 100)
dan:
substring(0, 100).concat("...")
Yang pertama:
hanya memotong
Yang kedua:
memotong + menambahkan ...
Tetapi ada satu masalah UX kecil:
Kalau description ternyata hanya 50 karakter, function ini tetap menghasilkan:
Description pendek...
padahal sebenarnya tidak terpotong.
Untuk project pembelajaran ini tidak masalah.
Nanti kalau ingin lebih realistis, kita bisa membuat:
kalau panjang > 100
potong + "..."
kalau tidak
tampilkan apa adanya
Misalnya secara konsep:
if (desc.length > 100) {
return desc.substring(0, 100).concat("...");
}
return desc;
Ini baru lebih cocok untuk aplikasi production.
🎯 Kesimpulan besar
Lecture ini sebenarnya bukan sekadar mengajarkan beberapa method baru. Yang paling penting adalah belajar menggabungkan method-method tersebut untuk menyelesaikan masalah nyata.
Untuk description:
desc
↓
substring(0, 100)
↓
ambil bagian awal
↓
concat("...")
↓
preview description
Untuk genre:
["action", "sci-fi", "drama"]
↓
map/flatMap
↓
["Action", "Sci-fi", "Drama"]
↓
join(", ")
↓
"Action, Sci-fi, Drama"
Dan hubungan dengan lecture sebelumnya menjadi:
String methods
├── substring()
├── slice()
├── at()
├── toUpperCase()
├── toLowerCase()
└── concat()
Array methods
├── map()
├── flatMap()
└── join()
Inti yang paling penting untuk kamu bawa: jangan hanya menghafal “substring() melakukan X” atau “map() melakukan Y”. Mulai biasakan berpikir:
“Saya punya data seperti apa → saya ingin hasil seperti apa → method apa yang bisa saya rangkai untuk mengubah data tersebut menjadi hasil yang saya inginkan?”
Itulah sebenarnya tujuan mulai masuk ke project seperti ini.