Iteraptor: perpustakaan untuk redus peta transparan dalam

Struktur data Elixir tidak dapat diubah. Ini bagus dari sudut pandang keyakinan bahwa data kami tidak akan rusak melebihi pengakuan dalam beberapa bagian kode yang tidak relevan, tetapi itu sedikit mengganggu ketika kita perlu mengubah struktur yang sangat bersarang.


Kami memiliki abstraksi Access brilian, yang sangat menyederhanakan empat operasi dasar pada objek yang sangat bersarang menggunakan fungsi default yang diekspor dari Kernel :



Keempat musketeer ini (dan d'Artagnan Kernel.get_and_update_in/{2,3} biasanya menggunakan sesuatu seperti ini:


 iex> users = %{"john" => %{age: 27, mood: ""}, "meg" => %{age: 23}} #   iex> get_in(users, ["john", :age]) #β‡’ 27 #   iex> put_in(users, ["john", :age], 28) #β‡’ %{"john" => %{age: 28, mood: ""}, "meg" => %{age: 23}} #   iex> update_in(users, ["john", :mood], & &1 + 1) #β‡’ %{"john" => %{age: 28, mood: ""}, "meg" => %{age: 23}} #   iex> pop_in(users, ["john", :mood]) #β‡’ {"", %{"john" => %{age: 27}, "meg" => %{age: 23}}} 

Nyaman, dan berfungsi dalam banyak kasus ... kecuali untuk yang tidak berfungsi. Untuk menggunakan Access , Anda perlu mengetahui lintasan ke elemen target, dan semua ini membutuhkan sejumlah kode boilerplate untuk memperbarui beberapa nilai yang bersarang sekaligus (misalnya, menghapus semua daun dengan nilai nil , atau membintangi isi semua bidang yang tidak perlu ditampilkan dalam log).


Untuk memberikan diskon grosir untuk bekerja dengan struktur bersarang, perpustakaan Iteraptor telah dibuat.


TL; DR:





Iterasi semua yang Anda bisa lakukan secara ajaib di Elixir . Untuk membuatnya iterable, cukup implementasikan protokol Enumerable untuk tipe khusus ini. Anda dapat mengelompokkan bagian-bagian ke dalam jalur pipa, memetakan, mengurangi, menyaring, menipiskan ... Maafkan bahasa Prancis saya, ya. Setiap orang yang menghabiskan setidaknya delapan jam dengan Elixir pasti melihat (dan bahkan mungkin menulis) sesuatu seperti ini:


 ~w|   | |> Enum.map(&String.capitalize/1) |> Enum.each(fn capitalized_name -> IO.puts "Hello, #{capitalized_name}!" end) # Hello, ! # Hello, ! # Hello, ! # Hello, ! 

Ini benar-benar sangat nyaman. Namun, kode dengan cepat menjadi rumit ketika datang ke struktur yang sangat bersarang, seperti peta dengan kata kunci bersarang, daftar, dll. Contoh yang baik dari ini adalah file konfigurasi yang berisi subbagian bersarang.


Jumlah pertanyaan pada Stack Overflow dengan pertanyaan "bagaimana saya bisa mengubah struktur bersarang?" Memaksa saya untuk akhirnya membuat perpustakaan ini. Implementasi di Elixir tampaknya agak membingungkan, karena semua yang ada di sana tidak dapat diubah, dan Anda tidak bisa begitu saja turun ke cabang struktur hingga ke daun, mengubah semua yang Anda butuhkan di tempat. Anda akan memerlukan baterai, karena, bagaimanapun, di bawah kap kode fungsional. Mengubah struktur bersarang mungkin adalah satu-satunya contoh yang pernah saya lihat dalam hidup saya ketika ketidakstabilan membuat segalanya lebih mudah.


Sebagai bonus untuk prekursor peta biasa, saya menambahkan implementasi untuk menyimpan nilai jauh di dalam struktur, yang menciptakan kunci menengah yang diperlukan. Itu berperilaku seperti yang diusulkan, tetapi ditolak di ruby ​​core Hash#bury . Pustaka ini juga tahu bagaimana "menjason" struktur bersarang yang mengandung kata kunci yang tidak dapat diserialisasi dalam json , karena di dalamnya disajikan sebagai daftar tupel dua elemen ( [foo: :bar] == [{:foo, :bar}] ), dan tupel tidak bersambung keluar dari kotak.




Jadi, mari kita sambut perpustakaan, yang beriterasi pada setiap peta / kata kunci / daftar di tail and Enum.each/2 standar Enum.each/2 dan Enum.map/2 .


Kemungkinan



Kata-kata tidak ada biaya, tunjukkan kodenya!


Iterasi, pemetaan, pengurangan


 # each iex> %{a: %{b: %{c: 42}}} |> Iteraptor.each(&IO.inspect(&1, label: "each"), yield: :all) # each: {[:a], %{b: %{c: 42}}} # each: {[:a, :b], %{c: 42}} # each: {[:a, :b, :c], 42} %{a: %{b: %{c: 42}}} # map iex> %{a: %{b: %{c: 42}}} |> Iteraptor.map(fn {k, _} -> Enum.join(k) end) %{a: %{b: %{c: "abc"}}} iex> %{a: %{b: %{c: 42}, d: "some"}} ...> |> Iteraptor.map(fn ...> {[_], _} = self -> self ...> {[_, _], _} -> "********" ...> end, yield: :all) %{a: %{b: "********", d: "some"}} # reduce iex> %{a: %{b: %{c: 42}}} ...> |> Iteraptor.reduce([], fn {k, _}, acc -> ...> [Enum.join(k, "_") | acc] ...> end, yield: :all) ...> |> :lists.reverse() ["a", "a_b", "a_b_c"] # map-reduce iex> %{a: %{b: %{c: 42}}} ...> |> Iteraptor.map_reduce([], fn ...> {k, %{} = v}, acc -> {​{k, v}, [Enum.join(k, ".") | acc]} ...> {k, v}, acc -> {​{k, v * 2}, [Enum.join(k, ".") <> "=" | acc]} ...> end, yield: :all) {​%{a: %{b: %{c: 42}}}, ["abc=", "ab", "a"]} # filter iex> %{a: %{b: 42, e: %{f: 3.14, c: 42}, d: %{c: 42}}, c: 42, d: 3.14} ...> |> Iteraptor.filter(fn {key, _} -> :c in key end, yield: :none) %{a: %{e: %{c: 42}, d: %{c: 42}}, c: 42} 

Bersarang dalam β†’ struktur rata dan sebaliknya


 iex> %{a: %{b: %{c: 42, d: [nil, 42]}, e: [:f, 42]}} ...> |> Iteraptor.to_flatmap(delimiter: "_") #β‡’ %{"a_b_c" => 42, "a_b_d_0" => nil, "a_b_d_1" => 42, "a_e_0" => :f, "a_e_1" => 42} iex> %{"abc": 42, "abd0": nil, "abd1": 42, "ae0": :f, "ae1": 42} ...> |> Iteraptor.from_flatmap #β‡’ %{a: %{b: %{c: 42, d: [nil, 42]}, e: [:f, 42]}} 

Roti


 iex> Iteraptor.jsonify([foo: [bar: [baz: :zoo], boo: 42]], values: true) %{"foo" => %{"bar" => %{"baz" => "zoo"}, "boo" => 42}} iex> Iteraptor.Extras.bury([foo: :bar], ~w|abcd|a, 42) [a: [b: [c: [d: 42]]], foo: :bar] 



Sumbernya terbuka , dokumentasinya cukup rinci , kami sudah berproduksi hampir dua tahun sekarang.


Selamat mencoba!

Source: https://habr.com/ru/post/id482478/


All Articles