Todo mundo que veio ao Elixir / Erlang de outras línguas provavelmente tem algumas expectativas em relação à forma como os operadores de comparação >
, <
, ==
etc. devem funcionar.Poderíamos esperar 1 < 2
, (e isso é realmente então). Em princípio, podemos dizer que a comparação funciona como deveria. Mas nem sempre.
No Elixir / Erlang, você pode comparar qualquer coisa. Geralmente. Enquanto para dois operandos do mesmo tipo, o resultado não é desencorajador, como no exemplo acima, a comparação de dois operandos de tipos diferentes leva a consequências bastante inesperadas. Porque os próprios tipos são "ordenados para comparação". Desta forma:
number < atom < reference < function < port < pid < tuple < map < list < bitstring
O que de repente leva ao fato de que a comparação completamente legítima 42 < nil
retorna true
.
Além disso, maps
(e, como resultado, structs
, que são na verdade os mesmos maps
sob o capô) são comparados de acordo com seus campos em ordem alfabética. Em outras palavras, as datas (que são implementadas no Elixir pela estrutura Date
) serão comparadas sequencialmente pelos valores do day
, month
e year
e year
, e isso provavelmente não é exatamente o que gostaríamos.
A partir da v1.10.0
, o Elixir fornece uma classificação conveniente com o padrão Enum.sort/2
se a estrutura exportar a função compare/2
:
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})
Qualquer módulo que define uma estrutura e exporta a função compare/2
pode ser passado como o segundo parâmetro para a chamada para Enum.sort/2
como está ou como uma tupla {:asc | :desc, StructModule}
{:asc | :desc, StructModule}
. Enum.sort/2
agora Enum.sort/2
inteligente o suficiente para compare/2
módulo passada para o Enum.sort/2
. A seguir, um trecho do módulo Enum
que implementa essa funcionalidade específica:
... 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)
Isso permite que você classifique corretamente as datas no exemplo abaixo (graças ao Date.compare/2
existente), bem como qualquer estrutura definida pelo usuário (se exportar compare/2
, é claro), como no exemplo da estrutura de User
acima.
dates = [~D[2019-01-01], ~D[2020-03-02], ~D[2019-06-06]] Enum.sort(dates)
Boa classificação!