Packages
map_sorter
0.1.13
0.2.70
0.2.69
0.2.68
0.2.67
0.2.66
0.2.65
0.2.64
0.2.63
0.2.62
0.2.61
0.2.60
0.2.59
0.2.58
0.2.57
0.2.56
0.2.55
0.2.54
0.2.53
0.2.52
0.2.51
0.2.50
0.2.49
0.2.48
0.2.47
0.2.46
0.2.45
0.2.44
0.2.42
0.2.41
0.2.40
0.2.39
0.2.38
0.2.37
0.2.36
0.2.35
0.2.34
0.2.33
0.2.32
0.2.31
0.2.30
0.2.29
0.2.28
0.2.27
0.2.26
0.2.25
0.2.24
0.2.23
0.2.22
0.2.21
0.2.20
0.2.19
0.2.18
0.2.17
0.2.16
0.2.15
0.2.14
0.2.13
0.2.12
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.22
0.1.21
0.1.20
0.1.19
0.1.18
0.1.17
0.1.16
0.1.15
0.1.14
0.1.13
0.1.12
0.1.11
0.1.10
0.1.9
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
Sorts a list of maps per a list of sort specs.
Current section
Files
Jump to
Current section
Files
lib/map_sorter/support.ex
defmodule MapSorter.Support do
@moduledoc """
Generates a sort function from a list of `sort specs`
(ascending/descending keys).
"""
require Logger
@type sort_dir :: :asc | :desc
@type sort_fun :: (map, map -> boolean)
@type sort_spec :: any | {sort_dir, any}
@doc """
Returns the AST of a sort function based on the given `sort specs`
(compile time or runtime).
## Examples
iex> alias MapSorter.Support
iex> Logger.configure(level: :info) # :debug → debug messages
iex> sort_fun_ast = Support.sort_fun_ast([:dob, desc: :likes])
iex> Logger.configure(level: :info) # :info → no debug messages
iex> match?({:&, _meta, _args}, sort_fun_ast)
true
iex> alias MapSorter.Support
iex> sort_specs_ast = quote do: Tuple.to_list({:dob, {:desc, :likes}})
iex> Logger.configure(level: :info) # :debug → debug messages
iex> sort_fun_ast = Support.sort_fun_ast(sort_specs_ast)
iex> Logger.configure(level: :info) # :info → no debug messages
iex> match?({{:., _, _}, _meta, _args}, sort_fun_ast)
true
"""
@spec sort_fun_ast([sort_spec] | {any, any, any}) :: {any, any, any}
def sort_fun_ast(sort_specs) when is_list(sort_specs) do
Logger.debug("expanding: sort_fun_ast(#{inspect(sort_specs)})...")
{:ok, sort_fun_ast} =
sort_specs
|> cond_fun_doc()
|> Code.string_to_quoted()
sort_fun_ast
end
def sort_fun_ast(sort_specs_ast) do
Logger.debug("injecting: eval_sort_fun(#{inspect(sort_specs_ast)})...")
quote do: MapSorter.Support.eval_sort_fun(unquote(sort_specs_ast))
end
@doc """
Returns a sort function based on the given `sort specs`.
## Examples
iex> alias MapSorter.Support
iex> Logger.configure(level: :info) # :debug → debug messages
iex> sort_fun = Support.eval_sort_fun([:dob, desc: :likes])
iex> Logger.configure(level: :info) # :info → no debug messages
iex> is_function(sort_fun, 2)
true
"""
@spec eval_sort_fun([sort_spec]) :: sort_fun
def eval_sort_fun(sort_specs) when is_list(sort_specs) do
Logger.debug("running: eval_sort_fun(#{inspect(sort_specs)})...")
{sort_fun, []} =
sort_specs
|> cond_fun_doc()
|> Code.eval_string()
sort_fun
end
@spec cond_fun_doc([sort_spec]) :: String.t
defp cond_fun_doc(sort_specs) do
cond_clauses_doc =
sort_specs
|> Enum.map_join(&cond_clauses_doc/1)
|> String.trim_trailing()
|> adapt()
here_doc =
"""
& cond do
#{cond_clauses_doc}
true -> true
end
"""
Logger.debug(here_doc)
here_doc
end
@spec cond_clauses_doc(sort_spec) :: String.t
defp cond_clauses_doc({:asc, key}) do
key = inspect(key)
"""
&1[#{key}] < &2[#{key}] -> true
&1[#{key}] > &2[#{key}] -> false
"""
end
defp cond_clauses_doc({:desc, key}) do
key = inspect(key)
"""
&1[#{key}] > &2[#{key}] -> true
&1[#{key}] < &2[#{key}] -> false
"""
end
defp cond_clauses_doc(key), do: cond_clauses_doc({:asc, key})
@doc """
Adapts the given `string` to invoke the `sortable/1` function.
## Examples
iex> alias MapSorter.Support
iex> Support.adapt(
...> \"""
...> &1[:dob] < ...
...> &2[:likes] -> ...
...> \"""
...> )
\"""
MapSorter.Support.sortable(&1[:dob]) < ...
MapSorter.Support.sortable(&2[:likes]) -> ...
\"""
"""
@spec adapt(String.t) :: String.t
def adapt(string) do
# &1[~D[2017-11-02]] < &2[~D[2017-11-02]] -> true
regex = ~r/(&[12]\[.+?])( +[<>-])/
String.replace(string, regex, "MapSorter.Support.sortable(\\1)\\2")
end
@doc """
Converts a value to a sortable format, if needed.
## Examples
iex> alias MapSorter.Support
iex> Support.sortable(~T[15:41:33])
"15:41:33"
iex> alias MapSorter.Support
iex> Support.sortable(3.1416)
3.1416
"""
@spec sortable(any) :: any
def sortable(%Date{} = value), do: Date.to_string(value)
def sortable(%DateTime{} = value), do: DateTime.to_string(value)
def sortable(%NaiveDateTime{} = value), do: NaiveDateTime.to_string(value)
def sortable(%Time{} = value), do: Time.to_string(value)
def sortable(value), do: value
end