Effektive Sortierung von Daten vom Typ Struct

Jeder, der aus anderen Sprachen zu Elixir / Erlang gekommen ist, hat höchstwahrscheinlich gewisse Erwartungen hinsichtlich der Funktionsweise der Vergleichsoperatoren > , < , == usw. Man würde 1 < 2 erwarten (und das ist wirklich so) so). Grundsätzlich kann man sagen, dass der Vergleich so funktioniert, wie er sollte. Aber nicht immer.


In Elixir / Erlang kann man alles vergleichen. Im Allgemeinen. Während für zwei Operanden desselben Typs das Ergebnis nicht entmutigend ist, wie im obigen Beispiel, führt der Vergleich zweier Operanden unterschiedlichen Typs zu eher unerwarteten Konsequenzen. Weil die Typen selbst "zum Vergleich geordnet" sind. Auf diese Weise:


 number < atom < reference < function < port < pid < tuple < map < list < bitstring 

Was plötzlich dazu führt, dass der völlig legitime Vergleich 42 < nil true .


Außerdem werden maps (und folglich structs , die eigentlich dieselben maps sind) in alphabetischer Reihenfolge nach ihren Feldern verglichen. Mit anderen Worten, Datumsangaben (die in Elixir durch die Date implementiert sind) werden nacheinander mit den Werten der Felder day , month und year verglichen, und dies ist höchstwahrscheinlich nicht ganz das, was wir möchten.


Ab v1.10.0 bietet Elixir eine komfortable Sortierung mit Standard- Enum.sort/2 wenn die Struktur die Funktion compare/2 exportiert:


 defmodule User do defstruct [:name] def compare(%User{name: n1}, %User{name: n2}) when n1 < n2, do: :lt def compare(%User{name: n1}, %User{name: n2}) when n1 > n2, do: :gt def compare(%User{}, %User{}), do: :eq end users = [ %User{name: "john"}, %User{name: "joe"}, %User{name: "jane"} ] Enum.sort(users, {:asc, User}) #⇒ [%User{name: "jane"}, # %User{name: "joe"}, # %User{name: "john"}] 

Jedes Modul, das eine Struktur definiert und die Funktion compare/2 exportiert, kann als zweiter Parameter entweder als Enum.sort/2 oder als Tupel {:asc | :desc, StructModule} an den Aufruf von Enum.sort/2 {:asc | :desc, StructModule} . Enum.sort/2 jetzt intelligent genug, compare/2 an Enum.sort/2 Moduls Enum.sort/2 . Das Folgende ist ein Auszug aus dem Enum Modul, das diese spezielle Funktionalität implementiert:


 ... defp to_sort_fun(module) when is_atom(module), do: &(module.compare(&1, &2) != :gt) defp to_sort_fun({:asc, module}) when is_atom(module), do: &(module.compare(&1, &2) != :gt) defp to_sort_fun({:desc, module}) when is_atom(module), do: &(module.compare(&1, &2) != :lt) 

Auf diese Weise können Sie die Daten im folgenden Beispiel (dank der vorhandenen Date.compare/2 ) sowie jede benutzerdefinierte Struktur (wenn sie natürlich compare/2 exportiert) korrekt sortieren, wie im obigen Beispiel mit der User Struktur.


 dates = [~D[2019-01-01], ~D[2020-03-02], ~D[2019-06-06]] Enum.sort(dates) # wrong #⇒ [~D[2019-01-01], ~D[2020-03-02], ~D[2019-06-06]] Enum.sort(dates, {:asc, Date}) # correct #⇒ [~D[2019-01-01], ~D[2019-06-06], ~D[2020-03-02]] 

Gute Sortierung!

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


All Articles