Packages
map_sorter
0.2.50
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/cond.ex
defmodule MapSorter.Cond do
@moduledoc """
Generates `cond/1` clauses as a heredoc from a list of sort specs.
"""
import MapSorter.SortSpec, only: [brackets: 1]
alias MapSorter.{SortSpec, SortSpecs}
@doc ~S'''
Generates `cond/1` clauses as a heredoc from a list of sort specs.
## Examples
iex> alias MapSorter.Cond
iex> sort_specs = [:name, {:desc, :dob}]
iex> Cond.clauses(sort_specs)
"""
&1[:name] < &2[:name] -> true
&1[:name] > &2[:name] -> false
&1[:dob] > &2[:dob] -> true
&1[:dob] < &2[:dob] -> false
"""
iex> alias MapSorter.Cond
iex> sort_specs = [:name, {:desc, {:dob, Date}}]
iex> Cond.clauses(sort_specs)
"""
&1[:name] < &2[:name] -> true
&1[:name] > &2[:name] -> false
&1[:dob] != nil and Date.compare(&1[:dob], &2[:dob]) == :gt -> true
&1[:dob] != nil and Date.compare(&1[:dob], &2[:dob]) == :lt -> false
"""
iex> alias MapSorter.Cond
iex> sort_specs = [:name, {:desc, {:dob, String}}]
iex> Cond.clauses(sort_specs)
"""
&1[:name] < &2[:name] -> true
&1[:name] > &2[:name] -> false
&1[{:dob, String}] > &2[{:dob, String}] -> true
&1[{:dob, String}] < &2[{:dob, String}] -> false
"""
'''
@spec clauses(SortSpecs.t()) :: String.t()
def clauses(sort_specs) when is_list(sort_specs) do
Enum.map_join(sort_specs, &do_clauses/1)
end
## Private functions
# Use short-circuit 'and' to prevent compare if key not in map...
@spec cond(SortSpec.key(), String.t(), atom) :: String.t()
defp cond(key, module, op) when op in [:lt, :gt] do
# brackets(:dob) => "[:dob]"
# brackets('dob') => "['dob']"
# brackets("dob") => ~s/["dob"]/
brk = brackets(key)
"""
&1#{brk} != nil and #{module}.compare(&1#{brk}, &2#{brk}) == #{inspect(op)}
"""
|> String.trim_trailing()
end
@spec do_clauses(SortSpec.t()) :: String.t()
defp do_clauses({:asc, {key, module}}) when is_atom(module) do
with {:module, module} <- Code.ensure_loaded(module),
true <- function_exported?(module, :compare, 2) do
module = "#{inspect(module)}"
"""
#{cond(key, module, :lt)} -> true
#{cond(key, module, :gt)} -> false
"""
else
_not_a_loaded_module_or_no_compare_function ->
# Then sort spec key is a 2-element tuple...
key = {key, module}
"""
&1#{brackets(key)} < &2#{brackets(key)} -> true
&1#{brackets(key)} > &2#{brackets(key)} -> false
"""
end
end
defp do_clauses({:desc, {key, module}}) when is_atom(module) do
with {:module, module} <- Code.ensure_loaded(module),
true <- function_exported?(module, :compare, 2) do
module = "#{inspect(module)}"
"""
#{cond(key, module, :gt)} -> true
#{cond(key, module, :lt)} -> false
"""
else
_not_a_loaded_module_or_no_compare_function ->
# Then sort spec key is a 2-element tuple...
key = {key, module}
"""
&1#{brackets(key)} > &2#{brackets(key)} -> true
&1#{brackets(key)} < &2#{brackets(key)} -> false
"""
end
end
defp do_clauses({:asc, key}) do
"""
&1#{brackets(key)} < &2#{brackets(key)} -> true
&1#{brackets(key)} > &2#{brackets(key)} -> false
"""
end
defp do_clauses({:desc, key}) do
"""
&1#{brackets(key)} > &2#{brackets(key)} -> true
&1#{brackets(key)} < &2#{brackets(key)} -> false
"""
end
defp do_clauses(key) do
do_clauses({:asc, key})
end
end