Iteraptor: Bibliothek fĂĽr Deep Transparent Map Redus

Elixier- Datenstrukturen sind unveränderlich. Dies ist vom Standpunkt der Überzeugung großartig, dass unsere Daten nicht bis zur Unkenntlichkeit in einem anderen irrelevanten Teil des Codes verfälscht werden, aber es ist ein wenig ärgerlich, wenn wir eine tief verschachtelte Struktur ändern müssen.


Wir haben eine brillante Access Abstraktion, die vier grundlegende Operationen für tief verschachtelte Objekte mithilfe der standardmäßig aus dem Kernel exportierten Funktionen erheblich vereinfacht:



Diese vier Musketiere (und d'Artagnan Kernel.get_and_update_in/{2,3} werden normalerweise wie Kernel.get_and_update_in/{2,3} verwendet:


 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}}} 

Es ist praktisch und funktioniert in vielen Fällen ... mit Ausnahme derer, bei denen es nicht funktioniert. Um Access , müssen Sie den Pfad zum Zielelement kennen, und all dies erfordert eine erhebliche Menge an Code, um mehrere verschachtelte Werte gleichzeitig zu aktualisieren (löschen Sie beispielsweise alle Blätter mit dem Wert nil oder markieren Sie den Inhalt aller Felder, die nicht in den Protokollen angezeigt werden müssen).


Um Rabatte für die Arbeit mit verschachtelten Strukturen zu gewähren, wurde die Iteraptor Bibliothek erstellt.


TL; DR:





Durchlaufen Sie bei Elixir alles, was Sie auf magische Weise durchlaufen können. Um es Enumerable zu machen, implementieren Enumerable einfach das Enumerable Protokoll für diesen bestimmten Typ. Sie können Passagen in Pipelines gruppieren, kartieren, reduzieren, filtern, ausdünnen ... Verzeihen Sie mein Französisch, ja. Jeder, der mindestens acht Stunden mit Elixir verbracht hat, hat definitiv so etwas gesehen (und möglicherweise sogar geschrieben):


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

Es ist wirklich sehr praktisch. Bei stark verschachtelten Strukturen, z. B. einer Map mit verschachtelten Schlüsselwörtern , Listen usw., wird der Code jedoch schnell umständlich. Ein gutes Beispiel hierfür ist jede Konfigurationsdatei, die verschachtelte Unterabschnitte enthält.


Die Anzahl der Fragen zum Stapelüberlauf mit der Frage "Wie kann ich die verschachtelte Struktur ändern?" Hat mich gezwungen, diese Bibliothek endgültig zu erstellen. Die Implementierung bei Elixir wirkt etwas verwirrend, da alles rundherum unveränderlich ist und Sie nicht einfach die Äste der Struktur bis zu den Blättern hinuntergehen und alles ändern können, was Sie an Ort und Stelle benötigen. Sie benötigen einen Akku, wie jedoch unter keiner Haube eines Funktionscodes. Das Ändern von verschachtelten Strukturen ist wahrscheinlich das einzige Beispiel, das ich in meinem Leben gesehen habe, wenn die Wandlungsfähigkeit die Dinge erleichtert.


Als Bonus für reguläre Kartenvorläufer habe ich eine Implementierung hinzugefügt, um den Wert tief in der Struktur zu speichern, wodurch bei Bedarf Zwischenschlüssel erstellt werden. Es verhält sich wie vorgeschlagen, wird aber in Ruby Core Hash#bury verworfen. Diese Bibliothek kann auch verschachtelte Strukturen mit Stichwörtern „jasonisieren“, die nicht einfach in json serialisiert werden können, da sie als Listen von Tupeln mit zwei Elementen dargestellt werden ( [foo: :bar] == [{:foo, :bar}] ). und Tupel sind nicht sofort serialisierbar.




Lassen Sie uns also die Bibliothek begrüßen, die über eine Karte / ein Schlüsselwort / eine Liste im Schwanz und in der Mähne iteriert und fast so einfach ist wie die Standard- Enum.each/2 und Enum.map/2 .


Die Möglichkeiten



Worte kosten nichts, zeig den Code!


Iteration, Mapping, Reduktion


 # 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} 

Tiefes Nesting → flache Struktur und umgekehrt


 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]}} 

Brötchen


 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] 



Die Quellen sind offen , die Dokumentation sehr detailliert , wir sind seit fast zwei Jahren in Produktion.


Habt eine gute Iteration!

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


All Articles