Packages
map_sorter
0.2.67
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.ex
defmodule MapSorter do
@moduledoc """
Sorts a list of maps per a list of sort specs.
Also supports:
- keywords
- structs implementing the `Access` behaviour
- nested maps, keywords or structs implementing the `Access` behaviour
"""
use PersistConfig
alias __MODULE__.{Log, SortSpecs}
@default_formatter Logger.default_formatter()
@logger_env get_app_env(:file_only_logger, :logger, [])
@doc """
Sorts `maps` per the given `sort_specs`.
Examples of sort specs for flat data structures:
```
- implicit: [:dob, :name]
- mixed: [:dob, desc: :name]
- explicit: [asc: :dob, desc: :name]
```
Examples of sort specs with a `Date` key for flat data structures:
```
- implicit: [{:dob, Date}, :name]
- mixed: [{:dob, Date}, desc: :name]
- explicit: [asc: {:dob, Date}, desc: :name]
```
Examples of sort specs for nested data structures:
```
- implicit: [[:birth, :date], :name]
- mixed : [[:birth, :date], desc: :name]
- explicit: [asc: [:birth, :date], desc: :name]
```
Examples of sort specs with a `Date` key for nested data structures:
```
- implicit: [{[:birth, :date], Date}, :name]
- mixed: [{[:birth, :date], Date}, desc: :name]
- explicit: [asc: {[:birth, :date], Date}, desc: :name]
```
## Examples
iex> require MapSorter
iex> people = [
...> %{name: "Mike", likes: "movies" , dob: "1992-04-15"},
...> %{name: "Mary", likes: "travels", dob: "1992-04-15"},
...> %{name: "Bill", likes: "karate" , dob: "1977-08-28"},
...> %{name: "Joe" , likes: "boxing" , dob: "1977-08-28"},
...> %{name: "Jill", likes: "cooking", dob: "1976-09-28"}
...> ]
iex> sort_specs = Tuple.to_list({:dob, {:desc, :likes}})
iex> sorted_people = %{
...> explicit: MapSorter.sort(people, asc: :dob, desc: :likes),
...> mixed: MapSorter.sort(people, [:dob, desc: :likes]),
...> runtime: MapSorter.sort(people, sort_specs)
...> }
iex> sorted_people.explicit == sorted_people.mixed and
...> sorted_people.explicit == sorted_people.runtime and
...> sorted_people.explicit
[
%{name: "Jill", likes: "cooking", dob: "1976-09-28"},
%{name: "Bill", likes: "karate" , dob: "1977-08-28"},
%{name: "Joe" , likes: "boxing" , dob: "1977-08-28"},
%{name: "Mary", likes: "travels", dob: "1992-04-15"},
%{name: "Mike", likes: "movies" , dob: "1992-04-15"}
]
"""
defmacro sort(maps, sort_specs) do
# To enforce logger configuration at compile time.
# Otherwise logger will use default configuration.
# dbg_handler_config("Before setting/adding logger handlers...", __CALLER__)
:ok = :logger.set_handler_config(:default, :formatter, @default_formatter)
for {:handler, id, :logger_std_h, config} <- @logger_env do
case :logger.add_handler(id, :logger_std_h, config) do
:ok -> :ok
{:error, {:already_exist, ^id}} -> :ok
end
end
# dbg_handler_config("After setting/adding logger handlers...", __CALLER__)
specs =
case sort_specs do
specs when is_list(specs) ->
# [asc: {:dob, {:__aliases__, [line: 7], [:Date]}}] =>
# [asc: {:dob, Date}]
{specs, []} = Code.eval_quoted(specs)
specs
specs ->
# In case any module attributes...
Macro.expand(specs, __CALLER__)
end
:ok = Log.debug(:sort_specs, {sort_specs, specs, __ENV__, __CALLER__})
case SortSpecs.to_quoted(specs) do
{:ok, fun_ast} ->
quote do: Enum.sort(unquote(maps), unquote(fun_ast))
{:error, invalid_specs} ->
:ok = Log.warning(:invalid_specs, {invalid_specs, __ENV__, __CALLER__})
maps
end
end
## Private functions
# defp get_handler_config(id, color) do
# case :logger.get_handler_config(id) do
# {:ok, %{config: %{type: :file, file: file}, level: _level}} ->
# file
# {:ok,
# %{
# config: %{type: :standard_io},
# level: level,
# formatter: {Logger.Formatter, %Logger.Formatter{colors: colors}}
# }} ->
# {id, level, colors[color]}
# {:error, {:not_found, ^id} = error} ->
# error
# end
# end
# @sep "==========================================================="
# @yellow "\e[33m"
# @light_yellow "\e[93m"
# @reset "\e[0m"
# defp dbg_handler_config(msg, env) do
# """
# #{@yellow}#{@sep}
# #{@light_yellow}#{msg}
# #{inspect(env.module)}.#{inspect(env.function)}:#{env.line}
# #{get_handler_config(:debug_handler, :debug) |> inspect()}
# #{get_handler_config(:default, :debug) |> inspect()}
# #{@yellow}#{@sep}#{@reset}
# """
# |> IO.puts()
# end
end