1. Pengantar
Di codelab Menggunakan jenis fungsi dan ekspresi lambda di Kotlin, Anda telah mempelajari fungsi tingkat tinggi, yang merupakan fungsi yang menggunakan fungsi lain sebagai parameter dan/atau menampilkan fungsi, seperti repeat()
. Fungsi tingkat tinggi sangat relevan dengan koleksi karena membantu Anda melakukan tugas umum, seperti mengurutkan atau memfilter, dengan lebih sedikit kode. Setelah Anda memahami dasar yang kuat untuk menggunakan koleksi, saatnya untuk meninjau kembali fungsi tingkat tinggi.
Dalam codelab ini, Anda akan mempelajari berbagai fungsi yang dapat digunakan pada jenis koleksi, termasuk forEach()
, map()
, filter()
, groupBy()
, fold()
, dan sortedBy()
. Dalam proses ini, Anda akan mendapatkan latihan tambahan menggunakan ekspresi lambda.
Prasyarat
- Pemahaman tentang jenis fungsi dan ekspresi lambda.
- Pemahaman tentang sintaksis lambda di akhir, seperti dengan fungsi
repeat()
. - Pengetahuan tentang berbagai jenis koleksi di Kotlin, seperti
List
.
Yang akan Anda pelajari
- Cara menyematkan ekspresi lambda ke dalam string.
- Cara menggunakan berbagai fungsi tingkat tinggi dengan koleksi
List
, termasukforEach()
,map()
,filter()
,groupBy()
,fold()
, dansortedBy()
.
Yang Anda butuhkan
- Browser web dengan akses ke Kotlin Playground.
2. forSetiap() dan template string dengan lambda
Kode awal
Pada contoh berikut, Anda akan mengambil List
yang merepresentasikan menu kue di toko roti (sungguh lezat!), dan menggunakan fungsi tingkat tinggi untuk memformat menu dengan cara yang berbeda.
Mulailah dengan menyiapkan kode awal.
- Buka Kotlin Playground.
- Di atas fungsi
main()
, tambahkan classCookie
. Setiap instanceCookie
merepresentasikan item pada menu, denganname
,price
, dan informasi lainnya tentang kue tersebut.
class Cookie(
val name: String,
val softBaked: Boolean,
val hasFilling: Boolean,
val price: Double
)
fun main() {
}
- Di bawah class
Cookie
, di luarmain()
, buat daftar kue seperti yang ditampilkan. Jenis ini disimpulkan sebagaiList<Cookie>
.
class Cookie(
val name: String,
val softBaked: Boolean,
val hasFilling: Boolean,
val price: Double
)
val cookies = listOf(
Cookie(
name = "Chocolate Chip",
softBaked = false,
hasFilling = false,
price = 1.69
),
Cookie(
name = "Banana Walnut",
softBaked = true,
hasFilling = false,
price = 1.49
),
Cookie(
name = "Vanilla Creme",
softBaked = false,
hasFilling = true,
price = 1.59
),
Cookie(
name = "Chocolate Peanut Butter",
softBaked = false,
hasFilling = true,
price = 1.49
),
Cookie(
name = "Snickerdoodle",
softBaked = true,
hasFilling = false,
price = 1.39
),
Cookie(
name = "Blueberry Tart",
softBaked = true,
hasFilling = true,
price = 1.79
),
Cookie(
name = "Sugar and Sprinkles",
softBaked = false,
hasFilling = false,
price = 1.39
)
)
fun main() {
}
Melakukan loop pada daftar dengan forEach()
Fungsi tingkat tinggi pertama yang Anda pelajari adalah fungsi forEach()
. Fungsi forEach()
menjalankan fungsi yang diteruskan sebagai parameter satu kali untuk setiap item dalam koleksi. Cara kerjanya mirip dengan fungsi repeat()
, atau loop for
. Lambda dieksekusi untuk elemen pertama, lalu elemen kedua, dan seterusnya, sampai dieksekusi untuk setiap elemen dalam koleksi. Tanda tangan metode adalah sebagai berikut:
forEach(action: (T) -> Unit)
forEach()
mengambil satu parameter tindakan—fungsi dari jenis (T) -> Unit
.
T
sesuai dengan jenis data apa pun yang ada dalam koleksi. Karena lambda mengambil satu parameter, Anda dapat menghilangkan nama dan merujuk ke parameter dengan it
.
Gunakan fungsi forEach()
untuk mencetak item dalam daftar cookies
.
- Di
main()
, panggilforEach()
di daftarcookies
, menggunakan sintaksis lambda di akhir. Karena lambda di akhir adalah satu-satunya argumen, Anda dapat menghilangkan tanda kurung saat memanggil fungsi.
fun main() {
cookies.forEach {
}
}
- Dalam isi lambda, tambahkan pernyataan
println()
yang mencetakit
.
fun main() {
cookies.forEach {
println("Menu item: $it")
}
}
- Jalankan kode Anda dan amati outputnya. Semua yang dicetak adalah nama jenis (
Cookie
), dan ID unik untuk objek, tetapi bukan konten objek.
Menu item: Cookie@5a10411 Menu item: Cookie@68de145 Menu item: Cookie@27fa135a Menu item: Cookie@46f7f36a Menu item: Cookie@421faab1 Menu item: Cookie@2b71fc7e Menu item: Cookie@5ce65a89
Menyematkan ekspresi dalam string
Saat pertama kali diperkenalkan ke template string, Anda melihat bagaimana simbol dolar ($
) dapat digunakan dengan nama variabel untuk menyisipkannya ke dalam string. Namun, cara ini tidak berfungsi seperti yang diharapkan saat dikombinasikan dengan operator titik (.
) untuk mengakses properti.
- Pada panggilan ke
forEach()
, ubah isi lambda untuk menyisipkan$it.name
ke dalam string.
cookies.forEach {
println("Menu item: $it.name")
}
- Jalankan kode. Perhatikan bahwa kode ini menyisipkan nama class,
Cookie
, dan ID unik untuk objek yang diikuti dengan.name
. Nilai propertiname
tidak diakses.
Menu item: Cookie@5a10411.name Menu item: Cookie@68de145.name Menu item: Cookie@27fa135a.name Menu item: Cookie@46f7f36a.name Menu item: Cookie@421faab1.name Menu item: Cookie@2b71fc7e.name Menu item: Cookie@5ce65a89.name
Untuk mengakses properti dan menyematkannya dalam string, Anda memerlukan ekspresi. Anda dapat membuat ekspresi menjadi bagian dari template string dengan mengapitnya menggunakan tanda kurung kurawal.
Ekspresi lambda ditempatkan di antara tanda kurung kurawal buka dan tutup. Anda dapat mengakses properti, melakukan operasi matematika, fungsi panggilan, dll., dan nilai hasil lambda disisipkan ke dalam string.
Mari kita ubah kode agar nama disisipkan ke dalam string.
- Mengapit
it.name
dalam tanda kurung kurawal untuk menjadikannya ekspresi lambda.
cookies.forEach {
println("Menu item: ${it.name}")
}
- Jalankan kode. Output-nya berisi
name
dari setiapCookie
.
Menu item: Chocolate Chip Menu item: Banana Walnut Menu item: Vanilla Creme Menu item: Chocolate Peanut Butter Menu item: Snickerdoodle Menu item: Blueberry Tart Menu item: Sugar and Sprinkles
3. map()
Fungsi map()
memungkinkan Anda mengubah koleksi menjadi koleksi baru dengan jumlah elemen yang sama. Misalnya, map()
dapat mengubah List<Cookie>
menjadi List<String>
yang hanya berisi name
kue, asalkan Anda memberi tahu fungsi map()
cara membuat String
dari setiap item Cookie
.
Misalkan Anda menulis aplikasi yang menampilkan menu interaktif untuk toko roti. Saat pengguna membuka layar yang menampilkan menu kue, mereka mungkin ingin melihat data yang ditampilkan secara logis, seperti nama yang diikuti dengan harga. Anda dapat membuat daftar string, yang diformat dengan data yang relevan (nama dan harga), menggunakan fungsi map()
.
- Hapus semua kode sebelumnya dari
main()
. Buat variabel baru bernamafullMenu
, dan tetapkan sama dengan hasil pemanggilanmap()
di daftarcookies
.
val fullMenu = cookies.map {
}
- Dalam isi lambda, tambahkan string yang diformat untuk menyertakan
name
danprice
dariit
.
val fullMenu = cookies.map {
"${it.name} - $${it.price}"
}
- Cetak konten
fullMenu
. Anda dapat melakukannya menggunakanforEach()
. KoleksifullMenu
yang ditampilkan darimap()
memiliki jenisList<String>
, bukanList<Cookie>
. SetiapCookie
dicookies
sesuai denganString
difullMenu
.
println("Full menu:")
fullMenu.forEach {
println(it)
}
- Jalankan kode. Outputnya akan cocok dengan konten daftar
fullMenu
.
Full menu: Chocolate Chip - $1.69 Banana Walnut - $1.49 Vanilla Creme - $1.59 Chocolate Peanut Butter - $1.49 Snickerdoodle - $1.39 Blueberry Tart - $1.79 Sugar and Sprinkles - $1.39
4. filter()
Fungsi filter()
memungkinkan Anda membuat subset koleksi. Misalnya, jika memiliki daftar angka, Anda dapat menggunakan filter()
untuk membuat daftar baru yang hanya berisi angka yang habis dibagi 2.
Sementara hasil dari fungsi map()
selalu menghasilkan koleksi dengan ukuran yang sama, filter()
menghasilkan koleksi dengan ukuran yang sama atau lebih kecil dari koleksi asli. Tidak seperti map()
, koleksi yang dihasilkan juga memiliki jenis data yang sama, sehingga memfilter List<Cookie>
akan menghasilkan List<Cookie>
lain.
Seperti map()
dan forEach()
, filter()
menggunakan ekspresi lambda tunggal sebagai parameter. Lambda memiliki parameter tunggal yang merepresentasikan setiap item dalam koleksi dan menampilkan nilai Boolean
.
Untuk setiap item dalam koleksi:
- Jika hasil ekspresi lambda adalah
true
, item tersebut akan disertakan dalam koleksi baru. - Jika hasilnya adalah
false
, item tidak disertakan dalam koleksi baru.
Hal ini berguna jika Anda ingin mendapatkan subset data di aplikasi Anda. Misalnya, toko roti tersebut ingin lebih menampilkan kue yang dipanggang lembut di bagian menu yang terpisah. Anda dapat filter()
daftar cookies
terlebih dahulu sebelum mencetak item.
- Di
main()
, buat variabel baru bernamasoftBakedMenu
, dan tetapkan ke hasil pemanggilanfilter()
di daftarcookies
.
val softBakedMenu = cookies.filter {
}
- Di bagian isi lambda, tambahkan ekspresi boolean untuk memeriksa apakah properti
softBaked
kue sama dengantrue
. KarenasoftBaked
adalahBoolean
itu sendiri, isi lambda hanya perlu berisiit.softBaked
.
val softBakedMenu = cookies.filter {
it.softBaked
}
- Cetak konten
softBakedMenu
menggunakanforEach()
.
println("Soft cookies:")
softBakedMenu.forEach {
println("${it.name} - $${it.price}")
}
- Jalankan kode. Menu dicetak seperti sebelumnya, tetapi hanya mencakup kue yang lembut.
... Soft cookies: Banana Walnut - $1.49 Snickerdoodle - $1.39 Blueberry Tart - $1.79
5. groupBy()
Fungsi groupBy()
dapat digunakan untuk mengubah daftar menjadi peta, berdasarkan fungsi. Setiap nilai unik yang ditampilkan dari fungsi tersebut akan menjadi kunci di peta yang dihasilkan. Nilai untuk setiap kunci adalah semua item dalam koleksi yang menghasilkan nilai unik yang ditampilkan tersebut.
Jenis data kunci sama dengan jenis nilai yang ditampilkan dari fungsi yang diteruskan ke groupBy()
. Jenis data nilai adalah daftar item dari daftar asli.
Hal ini mungkin sulit untuk dikonseptualisasikan, jadi mari kita mulai dengan contoh sederhana. Dengan daftar angka yang sama seperti sebelumnya, kelompokkan angka tersebut sebagai ganjil atau genap.
Anda dapat memeriksa apakah angkanya ganjil atau genap dengan cara membaginya dengan 2
dan memeriksa apakah sisanya 0
atau 1
. Jika sisanya 0
, angka itu genap. Namun, jika sisanya 1
, angka itu ganjil.
Hal ini dapat dilakukan dengan operator modulo (%
). Operator modulo membagi dividen di sisi kiri ekspresi dengan pembagi di sebelah kanan.
Operator modulo akan menampilkan sisanya, bukan menampilkan hasil pembagian, seperti operator pembagian (/
). Hal ini berguna untuk memeriksa apakah suatu angka genap atau ganjil.
Fungsi groupBy()
dipanggil dengan ekspresi lambda berikut: { it % 2 }
.
Peta yang dihasilkan memiliki dua kunci: 0
dan 1
. Setiap kunci memiliki nilai jenis List<Int>
. Daftar untuk kunci 0
berisi semua bilangan genap, sedangkan daftar untuk kunci 1
berisi semua bilangan ganjil.
Kasus penggunaan di dunia nyata adalah aplikasi foto yang mengelompokkan foto berdasarkan subjek atau lokasi tempat foto diambil. Untuk menu toko roti, mari kita kelompokkan menu berdasarkan apakah kue dipanggang lembut atau tidak.
Gunakan groupBy()
untuk mengelompokkan menu berdasarkan properti softBaked
.
- Hapus panggilan ke
filter()
dari langkah sebelumnya.
Kode yang akan dihapus
val softBakedMenu = cookies.filter {
it.softBaked
}
println("Soft cookies:")
softBakedMenu.forEach {
println("${it.name} - $${it.price}")
}
- Panggil
groupBy()
di daftarcookies
untuk menyimpan hasil dalam variabel bernamagroupedMenu
.
val groupedMenu = cookies.groupBy {}
- Teruskan ekspresi lambda yang menampilkan
it.softBaked
. Jenis nilai yang ditampilkan akan menjadiMap<Boolean, List<Cookie>>
.
val groupedMenu = cookies.groupBy { it.softBaked }
- Buat variabel
softBakedMenu
yang berisi nilaigroupedMenu[true]
, dan variabelcrunchyMenu
yang berisi nilaigroupedMenu[false]
. Karena hasil dari subskripMap
adalah nullable, Anda dapat menggunakan operator Elvis (?:
) untuk menampilkan daftar kosong.
val softBakedMenu = groupedMenu[true] ?: listOf()
val crunchyMenu = groupedMenu[false] ?: listOf()
- Tambahkan kode untuk mencetak menu kue yang lembut, diikuti dengan menu untuk kue yang renyah.
println("Soft cookies:")
softBakedMenu.forEach {
println("${it.name} - $${it.price}")
}
println("Crunchy cookies:")
crunchyMenu.forEach {
println("${it.name} - $${it.price}")
}
- Jalankan kode. Dengan menggunakan fungsi
groupBy()
, Anda membagi daftar menjadi dua, berdasarkan nilai salah satu properti.
... Soft cookies: Banana Walnut - $1.49 Snickerdoodle - $1.39 Blueberry Tart - $1.79 Crunchy cookies: Chocolate Chip - $1.69 Vanilla Creme - $1.59 Chocolate Peanut Butter - $1.49 Sugar and Sprinkles - $1.39
6. fold()
Fungsi fold()
digunakan untuk menghasilkan nilai tunggal dari koleksi. Fungsi ini paling sering digunakan untuk hal-hal seperti menghitung total harga, atau menjumlahkan semua elemen dalam daftar untuk menemukan rata-rata.
Fungsi fold()
mengambil dua parameter:
- Nilai awal. Jenis data disimpulkan saat memanggil fungsi (yaitu nilai awal
0
disimpulkan sebagaiInt
). - Ekspresi lambda yang menampilkan nilai berjenis sama dengan nilai awal.
Ekspresi lambda juga memiliki dua parameter:
- Yang pertama dikenal sebagai akumulator. Akumulator memiliki jenis data yang sama dengan nilai awal. Anggap saja ini sebagai total run. Setiap kali ekspresi lambda dipanggil, akumulator sama dengan nilai yang ditampilkan dari waktu sebelumnya lambda dipanggil.
- Yang kedua adalah jenis yang sama dengan setiap elemen dalam koleksi.
Seperti fungsi lain yang telah Anda lihat, ekspresi lambda dipanggil untuk setiap elemen dalam koleksi, sehingga Anda dapat menggunakan fold()
sebagai cara ringkas untuk menjumlahkan semua elemen.
Mari kita gunakan fold()
untuk menghitung harga total semua kue.
- Di
main()
, buat variabel baru bernamatotalPrice
dan tetapkan sama dengan hasil pemanggilanfold()
di daftarcookies
. Teruskan0.0
untuk nilai awal. Jenisnya disimpulkan sebagaiDouble
.
val totalPrice = cookies.fold(0.0) {
}
- Anda harus menentukan kedua parameter untuk ekspresi lambda. Gunakan
total
untuk akumulator, dancookie
untuk elemen koleksi. Gunakan tanda panah (->
) setelah daftar parameter.
val totalPrice = cookies.fold(0.0) {total, cookie ->
}
- Dalam isi lambda, hitung jumlah
total
dancookie.price
. Ini disimpulkan sebagai nilai yang ditampilkan dan diteruskan untuktotal
saat lambda dipanggil lagi.
val totalPrice = cookies.fold(0.0) {total, cookie ->
total + cookie.price
}
- Cetak nilai
totalPrice
, yang diformat sebagai string untuk keterbacaan.
println("Total price: $${totalPrice}")
- Jalankan kode. Hasilnya harus sama dengan jumlah harga di daftar
cookies
.
... Total price: $10.83
7. sortedBy()
Saat pertama kali mempelajari koleksi, Anda mengetahui bahwa fungsi sort()
dapat digunakan untuk mengurutkan elemen. Namun, tindakan ini tidak akan berfungsi pada koleksi objek Cookie
. Class Cookie
memiliki beberapa properti dan Kotlin tidak akan mengetahui properti mana (name
, price
, dll.) yang ingin Anda urutkan.
Untuk kasus ini, koleksi Kotlin menyediakan fungsi sortedBy()
. sortedBy()
memungkinkan Anda menentukan lambda yang menampilkan properti yang ingin Anda urutkan. Misalnya, jika Anda ingin mengurutkan berdasarkan price
, lambda akan menampilkan it.price
. Selama jenis data nilai memiliki tata urutan alami—string diurutkan menurut abjad, nilai numerik diurutkan dalam urutan menaik—string akan diurutkan seperti koleksi jenis tersebut.
Anda akan menggunakan sortedBy()
untuk mengurutkan daftar kue menurut abjad.
- Di
main()
, setelah kode yang ada, tambahkan variabel baru bernamaalphabeticalMenu
dan tetapkan agar sama dengan memanggilsortedBy()
di daftarcookies
.
val alphabeticalMenu = cookies.sortedBy {
}
- Dalam ekspresi lambda, tampilkan
it.name
. Daftar yang dihasilkan akan tetap berjenisList<Cookie>
, tetapi diurutkan berdasarkanname
.
val alphabeticalMenu = cookies.sortedBy {
it.name
}
- Cetak nama kue dalam
alphabeticalMenu
. Anda dapat menggunakanforEach()
untuk mencetak setiap nama pada baris baru.
println("Alphabetical menu:")
alphabeticalMenu.forEach {
println(it.name)
}
- Jalankan kode. Nama kue dicetak dalam urutan abjad.
... Alphabetical menu: Banana Walnut Blueberry Tart Chocolate Chip Chocolate Peanut Butter Snickerdoodle Sugar and Sprinkles Vanilla Creme
8. Kesimpulan
Selamat! Anda baru saja melihat beberapa contoh bagaimana fungsi tingkat tinggi dapat digunakan dengan koleksi. Operasi umum, seperti pengurutan dan pemfilteran, dapat dilakukan dalam satu baris kode, membuat program Anda lebih ringkas dan ekspresif.
Ringkasan
- Anda dapat melakukan loop pada setiap elemen dalam koleksi menggunakan
forEach()
. - Ekspresi dapat disisipkan ke dalam string.
map()
digunakan untuk memformat item dalam koleksi, sering kali sebagai koleksi jenis data lain.filter()
dapat membuat subset koleksi.groupBy()
membagi koleksi berdasarkan nilai fungsi yang ditampilkan.fold()
mengubah koleksi menjadi satu nilai.sortedBy()
digunakan untuk mengurutkan koleksi berdasarkan properti tertentu.