Halo, Habr!
Nama saya Alexander Zimin, saya adalah pengembang iOS di Badoo. Ini adalah terjemahan dari artikel oleh kolega saya, Schwib, di mana dia menggambarkan seperti apa fungsi flatMap di Swift dan mengapa salah satu kelebihannya dinamai compactMap. Artikel ini bermanfaat baik untuk memahami proses yang terjadi di 
repositori Swift dan 
evolusinya , serta untuk pengembangan umum.

Dalam pemrograman fungsional, ada definisi yang jelas tentang apa fungsi 
flatMap . Metode 
flatMap mengambil daftar dan fungsi transformasi (yang untuk setiap transformasi mengharapkan untuk mendapatkan nilai nol atau lebih), menerapkannya ke setiap elemen daftar dan membuat daftar tunggal (diratakan). Perilaku ini berbeda dari fungsi 
map sederhana, yang menerapkan transformasi untuk setiap nilai dan berharap untuk mendapatkan hanya satu nilai untuk setiap transformasi.

Untuk beberapa versi, Swift memiliki 
map dan 
flatMap . Namun, di 
Swift 4.1 Anda tidak bisa lagi menerapkan 
flatMap ke urutan nilai dan masih melewati penutupan yang mengembalikan nilai opsional. Sekarang ada metode 
compactMap untuk 
compactMap .
Pada awalnya mungkin tidak begitu mudah untuk memahami esensi dari inovasi. Jika 
flatMap bekerja dengan baik, mengapa memperkenalkan metode terpisah? Mari kita cari tahu.
Perpustakaan standar Swift sebelum versi 4.1 memberikan tiga implementasi kelebihan beban untuk 
flatMap :
 1. Sequence.flatMap<S>(_: (Element) -> S) -> [S.Element],  S : Sequence 2. Optional.flatMap<U>(_: (Wrapped) -> U?) -> U? 3. Sequence.flatMap<U>(_: (Element) -> U?) -> [U] 
Mari kita pergi melalui ketiga opsi dan melihat apa yang mereka lakukan.
Sequence.flatMap <S> (_: (Elemen) -> S) -> [S.Element], di mana S: Sequence
Kelebihan pertama adalah untuk urutan di mana penutupan mengambil elemen dari urutan itu dan mengubah ke urutan lain.
flatMap - 
flatMap semua urutan yang diubah ini ke dalam urutan akhir yang dikembalikan sebagai hasilnya. Sebagai contoh:
 let array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] let flattened = array.flatMap { $0 }  
Ini adalah contoh yang bagus tentang bagaimana metode 
flatMap seharusnya bekerja. Kami akan mengubah (memetakan) setiap elemen dari daftar sumber dan membuat urutan baru. Berkat 
flatMap hasil akhirnya adalah struktur rata dari sekuens yang ditransformasikan.
Opsional.flatMap <U> (_: (Dibungkus) -> U?) -> U?
Kelebihan kedua adalah untuk tipe opsional. Jika jenis opsional yang Anda panggil memiliki nilai, maka penutupan akan dipanggil dengan nilai tanpa pembungkus opsional (nilai yang tidak dibuka), dan Anda dapat mengembalikan nilai opsional yang dikonversi.
 let a: Int? = 2 let transformedA = a.flatMap { $0 * 2 }  
Sequence.flatMap <U> (_: (Elemen) -> U?) -> [U]
Kelebihan ketiga akan membantu Anda memahami apa 
compactMap . Versi ini terlihat sama dengan yang pertama, tetapi ada perbedaan penting. Dalam hal ini, penutupan mengembalikan opsional. 
flatMap memprosesnya, melewatkan nilai-nilai nil yang dikembalikan, dan memasukkan semua sisanya - dalam hasilnya sebagai nilai tanpa pembungkus.
 let array = [1, 2, 3, 4, nil, 5, 6, nil, 7] let arrayWithoutNils = array.flatMap { $0 }  
Namun dalam kasus ini, pemesanan tidak dilakukan. Oleh karena itu, versi 
flatMap lebih dekat dengan 
map daripada definisi fungsional 
flatMap . Dan masalah dengan kelebihan ini adalah bahwa Anda mungkin tidak menggunakannya dengan benar di mana 
map akan bekerja dengan sempurna.
 let array = [1, 2, 3, 4, 5, 6] let transformed = array.flatMap { $0 }  
Penggunaan 
flatMap berhubungan dengan kelebihan ketiga, secara implisit membungkus nilai yang dikonversi dalam opsional, dan kemudian menghapus pembungkus untuk menambah hasilnya. Situasi menjadi sangat menarik jika konversi string tidak digunakan dengan benar.
 struct Person { let name: String } let people = [Person(name: βFooβ), Person(name: βBarβ)] let names = array.flatMap { $0.name } 
Di Swift sebelum versi 4.0, kita akan mendapatkan konversi ke 
[βFooβ, βBarβ] . Tetapi dimulai dengan versi 4.0, nilai string mengimplementasikan protokol Collection. Oleh karena itu, penggunaan 
flatMap dalam kasus ini, alih-alih beban ketiga, akan sesuai dengan yang pertama, dan kami akan mendapatkan hasil "yang diratakan" dari nilai yang dikonversi: 
[βFβ, βoβ, βoβ, βBβ, βaβ, βrβ]Saat memanggil 
flatMap Anda tidak akan mendapatkan kesalahan, karena ini diperbolehkan digunakan. Tetapi logikanya akan rusak, karena hasilnya adalah tipe 
Array<Character>.Type , bukan 
Array<String>.Type diharapkan 
Array<String>.Type .
Kesimpulan
Untuk menghindari penyalahgunaan 
flatMap , versi kelebihan beban ketiga telah dihapus dari versi Swift baru. Dan untuk mengatasi masalah yang sama (menghapus nil-nilai) sekarang Anda perlu menggunakan metode terpisah - 
compactMap .