
FlatMap dan Map
Beberapa pemrograman tertentu sering menggunakan method tersebut untuk memodifikasi data, sering disebut sebagai operator. Prinsipnya pemrograman yang melakukan FlatMap dan Map termasuk dalam functional programming. Sebagai contoh pada Java 8 yang sering disebut sebagai lambda, jika didalam swift disebut sebagai Closure. Apalagi dalam library milik Reactive Extensions alias Rx maka akan menjumpai emitting data menggunakan FlatMap dan Map. Keduanya sama-sama memakai Stream<T> dan menghasilkan keluaran berupa Stream<R>, perbedaan mencolok antara Map dan FlatMap hasil keluarannya. Perlu diketahui bahwa masing-masing bahasa memiliki karakteristik berbeda dalam menerapkan Map dan FlatMap. Oke selanjutnya kita harus cari tahu perbedaan mendasar dari dua buah method ini.
Dalam kasus ini, Map bukanlah kelas pada API Java Collection melainkan method untuk mengoperasikan dan mendapatkan fungsi, dimana selalu dipanggil setiap ada nilai input Stream. Kemudian menghasilkan satu keluaran, yang nantinya memodifikasi/menstransformasi Stream output. Dalam tulisan kali ini penulis tidak membahasa secara detail mengenai Closure dan lambda seperti apa, silakan teman-teman bisa cek di dokumentasi masing bahasa pemrograman.
Map & FlatMap di Swift
Berikut contoh pada Swift Programming yang kita kenal sebagai Closure,
// menginisialisasi array
let digitDict = ["nol","satu","dua","tiga","empat","lima"]//menyalin array kemudian memodifikasinya
let stringAngka = digitDict.map{
xAngka -> String in
return "Nomor urut Anda adalah \(xAngka)"
}//mendapatkan isi dalam array stringAngka
for (index,item) in stringAngka.enumerated() {
print("\(item) atau \(index)")
}
output:
Nomor urut Anda adalah nol atau 0
Nomor urut Anda adalah satu atau 1
Nomor urut Anda adalah dua atau 2
Nomor urut Anda adalah tiga atau 3
Nomor urut Anda adalah empat atau 4
Nomor urut Anda adalah lima atau 5Kode diatas mentransformasikan Integer ke dalam bentuk String dengan Map. Perlu diingat bahwa implementasi Closure mirip Lambda sehingga ketika Anda memakai IDE dan otomasi completions aktif perlu di hapus. Karena otomasi Completions menggenerate kurung biasa bukan dengan kurung kurawa, sehingga terjadi kesalahan, hal ini sering terjadi oleh para developer. Lambda dan closure sudah tidak memerlukan parameter lagi sehingga tidak memerlukan kurung biasa melainkan kurung kurawa. Kita kembali menelaah kode diatas.
Method enumerated() digunakan untuk menggunakan index pada parameter dan appending() digunakan untuk menambahkan string mirip dengan operator “+” pada String, kemudian “\(“string”)” adalah penggunaan String interpolation yakni memperoleh objek/fungsi di dalam String “ ” mirip dengan Kotlin perbedaannya symbol yang digunakan pada prefix jika Swift menggunakan symbol “\”, kotlin dengan “$”. Map memodifikasi atau mentransform array Integer kedalam bentuk array String, dan yang paling keren dalam Map adalah Map Chaining yakni map berantai, sebagai contoh berikut
// menginisialisasi array
let digitDict = [“nol”,”satu”,”dua”,”tiga”,”empat”,”lima”]//menyalin array kemudian memodifikasinya
let stringAngka = digitDict.map{
xAngka -> String in
return “Nomor urut Anda adalah \(xAngka)”
}.map{stringAng -> String in return stringAng.appending(“(dan)”) }
.map{stringAn -> String in return stringAn.appending(“(lagi)”)}//mendapatkan isi dalam array stringAngka
for (index,item) in stringAngka.enumerated() {
print(“\(item) atau \(index)”)}
output:
Nomor urut Anda adalah nol(dan)(lagi) atau 0
Nomor urut Anda adalah satu(dan)(lagi) atau 1
Nomor urut Anda adalah dua(dan)(lagi) atau 2
Nomor urut Anda adalah tiga(dan)(lagi) atau 3
Nomor urut Anda adalah empat(dan)(lagi) atau 4
Nomor urut Anda adalah lima(dan)(lagi) atau 5Berikutnya kita coba ganti method Map{} dengan flatmap{}, bagaimana hasilnya apakah masih sama? Hasilnya tetap sama dengan Map. Mari kita coba dengan kode lain sebagai berikut,
/*Membuat array didalam array lalu mentransformasikan nilai menjadi dua kalinya nilai pertama dengan map{}*/var arrayOf = [[1,1],[2,2],[3,3]].map{
array in return array.map{
integer in return integer * 2
}
}print(arrayOf)
output:
[[2, 2], [4, 4], [6, 6]]Kemudian kita ganti Map dengan flatMap,
var arrayOf = [[1,1],[2,2],[3,3]].flatMap{
array in return array.map{
integer in return integer * 2
}
}print(arrayOf)
output:
[2, 2, 4, 4, 6, 6]Nah kesimpulannya apa dari dua kode diatas? Masing-masing menggunakan map dan flatMap, ketika method Map diimplementasikan maka tidak ada perbedaan berarti dari struktur awal data, namun flatMap menghasilkan struktur yang berbeda yakni dengan menggabungkan banyak array (nested) menjadi satu kesatuan Array.
Map & flatMap di Java 8
Kita lakukan percobaan di Java 8 perhatikan kodingan berikut ini,
public class coba {
private static String [] myArray = { “nol”, “satu”, “dua”, “tiga”, “empat”,”lima”};
public static void main(String[] args) {
Arrays.stream(myArray).map( stringAngka -> “Urutan Anda adalah “ + stringAngka).forEach(System.out::println);
}
}output:
Urutan Anda adalah nol
Urutan Anda adalah satu
Urutan Anda adalah dua
Urutan Anda adalah tiga
Urutan Anda adalah empat
Urutan Anda adalah limaKonsepnya hampir sama dengan Swift namun implementasi di Java 8 berbeda. Pertama menginisialisasikan array dengan type String kemudian membuat mendefinisikan Array.stream() supaya Array bisa melakukan Stream. Langkah selanjutnya menstransformasikan/memodifikasi String awal menjadi String baru dengan penambahan String. Kemudian melakukan Iterasi dengan method forEach guna mendapatkan isi dalam Stream lalu menampilkannya menggunakan method references System.out::println(). Sekilas prinsipnya sama yakni mentransformasi/memodifikasi data yang di emit kemudian menghasilkan keluaran yang sudah dimodifikasi oleh Map. Map ini pun bisa di-Chaining sama dengan Swift.
public class coba {
private static String [] myArray = { "nol", "satu", "dua", "tiga", "empat","lima"};public static void main(String[] args) {
Arrays.stream(myArray).map( stringAngka -> "Urutan Anda adalah " + stringAngka).map(stringAn -> stringAn + "(dan)")
.map(stringA -> stringA + "(lagi)")
.forEach(System.out::println);
}
}
output:
Urutan Anda adalah nol(dan)(lagi)
Urutan Anda adalah satu(dan)(lagi)
Urutan Anda adalah dua(dan)(lagi)
Urutan Anda adalah tiga(dan)(lagi)
Urutan Anda adalah empat(dan)(lagi)
Urutan Anda adalah lima(dan)(lagi)Oke, selanjutnya kita mencoba untuk menggunakan method flatMap{}. Guna mengimplementasi method flatMap maka perlu adanya nested, konsep ini juga sama dengan Swift namun implementasinya berbeda. Pada Java 8 lebih menggunakan List dalam List supaya Stream bisa dilakukan. Intinya Stream parent bisa digabung dengan subList Stream. Sebaiknya kita langsung ke praktiknya.
Hal yang dilakukan pertama adalah membuat kelas yang nanti menjadi generik pada List,
class Developer {
private String name;
private Set<String> language;
public Developer(String name){
this.name = name;
this.language = new HashSet<>();
}
public void addAll(List<String> list){
language.addAll(list);
}
public void add(String language){
this.language.add(language);
}
public Set<String> getLanguage(){
return language;
}
}Nah disana ada Objek language yang bertype Set<String> nantinya yang berlaku sebagai subStream. Selanjutnya kita kembali ke Main kelas,
public class coba {private static String [] myLanguage = {"Java","Swift","Kotlin","Clojure","Haskell","Go","Java Script"};
public static void main(String[] args) {
List<String> listLanguage = Arrays.asList(myLanguage);
Developer language = new Developer("Ran");
language.addAll(listLanguage);
List<Developer> listDeveloper = new ArrayList<>();
listDeveloper.add(language);
List<String> team = listDeveloper.stream()
.map(dev -> dev.getLanguage())
.flatMap(lang -> lang.stream())
.collect(Collectors.toList());team.forEach(System.out::println);
}
}
output:
Java
Java Script
Go
Haskell
Swift
Kotlin
ClojureNah selanjutnya menginisialisasi Array bertype String didalam ada berbagai bahasa dalam bentuk String. Beralih ke main method disana Array “myLanguage” di masukkan dalam bentuk List dengan nama listLanguage menggunakan method Arrays.asList(). Selanjutnya mendeklarasikan kelas Developer lalu mengisi kelas Developer tadi dengan list language. Kemudian baru List dengan generic Developer di deklarasikan kemudian diisi dengan language developer. Di sini diperlukan praktik agar mengetahui dan cobalah untuk memodifikasi beberapa hal diatas. Nah, Stream pun siap diimplementasikan, dengan membuat List baru dengan tipe String didalamnya memuat listDeveloper Stream, map bertuga untuk mengubah dari listDeveloper dengan tipe Developer ke dalam list bertipe Set<String> menggunakan getLanguage. Kemudian flatMap siap beraksi, flatMap mengakses subList tadi yakni Set<String> language dan melakukan Stream. Kemudian diperlukan method collect() untuk memasukkan isi dari langStream ke dalam list Kolekor.
Selanjutnya ditampilkan menggunakan method forEach yang di dalamnya terdapat method references Println(). Kesimpulannya hampir sama dengan Swift yakni mengakses dan menyatukan subList didalam agar bisa diakses diluar menjadi satu bagian yang utuh. Bahasa lainnya men-flat-kan semua yang ada didalam StreamList. Secara garis besar implementasi Map dan FlatMap di Java sama dengan di Kotlin.
Map & flatMap RxJava
Kali ini kita akan membahas RxJava yang popular di mobile Android Developer, dengan adanya RxJava kita akan mengenal banyak istilah penting seperti Observable, Flowable, Backpressure dll. Namun, hal tersebut tidak dibahas disini melainkan Map dan flatMap RxJava.
Jika ada Lambda dan Closere berarti secara tidak sadar kita sudah mengenal Higher Order function dimana mentranformasi dari nilai inputan seperti contoh-contoh sebelumnya. Nah di RxJava, High Order Function dapat diamati pada suatu instance/object, menghasilkan instance observable baru yang disebut sebagai operator. Supaya lebih paham mengenai instance observable map perhatikan gambar berikut ini.

ilustrasi kotak yang berada ditengah adalah operator (function). Operator tersebut menstranformasikan input (dalam bentuk lingkaran atas) ke dalam bentuk lain (dalam bentuk segitiga bawah). Gambar panah diatas kotak tengah sebagai sumber Observable instance, lalu lingkaran yang berwarna sebagai onNext notification, lalu garis vertical yang mengarah ke kanan sebagai onComplete notification. Sedangkan anak panah di bawah kotak tengah sebagai keluaran dari Observable instance yang sudah ditranformasikan.
Kita coba implementasikan, Map operator menstranformasikan setiap nilai ‘next’ dari pusat untuk diubah ke bentuk lain contoh berikut Map menstranformasikan dari Integer ke dalam bentuk String
public class tryRxJava {
public static void main(String[] args) {
Observable<String> map = Observable
.just(1,2,3,4,5,6,7,8,9,10)
.map(v -> v * 3)
.map(v -> (v%2==0)? " Genap" : "Ganjil");map.subscribe(System.out::println);
}
}
Output:
Ganjil
Genap
Ganjil
Genap
Ganjil
Genap
Ganjil
Genap
Ganjil
GenapBaiklah, mari kita telaah kode diatas. Observable dengan type String dideklarasikan dengan method just berisi data Integer yang ingin ditransformasikan lalu method map menstransformasikan setiap data yang diemmit menjadi nilai perkalian tiga. Kemudian method map yang kedua mentransformasikan integer dalam bentuk String yang didalamnya ada operator ternary, operator ternary tersebut mengkondisikan value yang diemmit apakah ganjil atau genap berdasarkan hasil modulus sama dengan nol.
Dengan menggunakan operator map(), kita dapat menstranformasikan nilai data yang diemmit menjadi nilai baru. Berikutnya kita akan membahas flatMap dimana lebih powerfull dibandingkan map, tetapi dengan pengguanan dan tujuan yang berbeda.
Berikut berpedaan Map dengan flatMap di RxJava operator.
FlatMap operator selalu menstranformasikan nilai atau sequence nilai kedalam bentuk Observable instance, hal ini berbeda dengan map yang hanya mentranformasikan dalam bentuk atau type data lain.
FlatMap me-merge nilai yang telah diemmit oleh hasil Observable instances, Artinya disamping menghasilkan observable FlatMap juga memberikan Observable instance sebagai nilai dan mengemmit notifikasi Observable.

Sesuai dengan yang Anda lihat, setiap nilai dari sumber instances observable berubah menjadi instance observable yang lain. FlatMap operator sangat berguna untuk proses logika, sebagia contohnya jika instances observable merepresentasikan file system folder dan mengemmit file tersebut, dan mentransformasikan setiap objek file kedalam bentuk instances observable. Tak hanya itu, ada intances observable yang khusus menangani persoalan tersebut salah satunya adalah file observable.
Operator FlatMap memiliki muiltiple overload, yakni mampu menjalankan 3 fungsi utama observable secara bersamaan. Sebagai contohnya adalah masing-masing fungsi onNext, onComplete, dan OnError berjalan serempak, 3 fungsi tersebut dapat ditransformasikan kedelam masing-masing fungsi yang berbeda, bagusnya apabila terjadi onError dan onComplete, instance observable yang sudah ditransformasikan akan di-merge digabungkan pada hasil akhir instances observable dengan diikuti notification onComplete, berikut adalah contoh Kodenya
public class tryRxJava {public static void main(String[] args) {
Observable<Integer> flatMap = Observable.just(-1, 0, 1)
.map( v -> 2/v)
.flatMap(v -> Observable.just(v),
e -> Observable.just(0),
()-> Observable.just(42)
);flatMap.forEach(System.out::println);
}
}
output:
-2
0Nah, mengenai kode diatas hasil keluarannya adalah -2 dan 0, kenapa? Karena saat data diemmit dari Observabel just yakni -1,0, dan 1error terjadi pada nilai 0 akibat penjumlahan yang tak terhingga (2/0) sehingga nilai 1 tidak akan mencapai hasil keluaran. Kenapa ada angka 42 disana? Itu sekadar contoh saja 42 akan diemmit ketika onComplete berlangsung. Karena terjaid Error maka OnComplete tidak dilanjutkan, dan hanya meng-emmit nilai 0 akibat Error.
Kesimpulan
Oke tibalah pada akhir tulisan ini, kesimpulan dari tulisana diatas mengenai Map dan FlatMap. FlatMap dalam bahasa pemrograman Swift dan Java/Kotlin terletak pada nested content, jika masing-masing memiliki List memiliki subList maka FlatMap akan menyatukan menjadi satu kesatuan atau mentranformasikan menjadi satu List utuh, sedangkan Map cukup menampilkan apa adanya apabila didalam List atau Array terdapat List atau Array juga maka mereka akan berlaku generik untuk List kemudian berlaku array lebih dari satu dimensi. Kemudian Map dan FlatMap pada Observable atau RxJava/Kotlin perbedaan keduanya terletak pada return value, dimana FlatMap mampu mengembalikan return value berupa Observable sedangkan pada Map hanya mengembalikan dalam bentuk Type data biasa. Seperti itulah perbedaannya.
