Packages
cldr_utils
2.15.1
2.29.7
2.29.6
2.29.5
2.29.4
2.29.3
2.29.2
2.29.1
2.29.0
2.28.3
2.28.2
2.28.1
2.28.0
2.27.0
2.26.0
2.25.0
2.24.2
2.24.1
2.24.0
2.23.1
2.23.0
2.22.0
2.21.0
2.20.0
2.19.2
2.19.1
2.19.0
2.18.0
2.17.2
2.17.1
2.17.0
2.17.0-rc.0
2.16.0
2.15.1
2.15.0
2.14.1
2.14.0
2.13.3
retired
2.13.2
2.13.1
2.13.0
retired
2.12.0
2.11.0
2.10.0
2.9.1
2.9.0
retired
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.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
Map, Calendar, Digits, Decimal, HTTP, Macro, Math, and String helpers for ex_cldr.
Current section
Files
Jump to
Current section
Files
lib/cldr/utils/map.ex
defmodule Cldr.Map do
@moduledoc """
Functions for transforming maps, keys and values.
"""
@doc """
Returns am argument unchanged.
Useful when a `noop` function is required.
"""
def identity(x), do: x
@default_deep_map_options [level: 1..1_000_000, filter: [], reject: [], skip: [], only: [], except: []]
@starting_level 1
@max_level 1_000_000
@doc """
Recursively traverse a map and invoke a function
that transforms the mapfor each key/value pair.
## Arguments
* `map` is any `t:map/0`
* `function` is a `1-arity` function or function reference that
is called for each key/value pair of the provided map. It can
also be a 2-tuple of the form `{key_function, value_function}`
* In the case where `function` is a single function it will be
called with the 2-tuple argument `{key, value}`
* In the case where function is of the form `{key_function, value_function}`
the `key_function` will be called with the argument `key` and the value
function will be called with the argument `value`
* `options` is a keyword list of options. The default is
`#{inspect @default_deep_map_options}`
## Options
* `:level` indicates the starting (and optionally ending) levels of
the map at which the `function` is executed. This can
be an integer representing one `level` or a range
indicating a range of levels. The default is `1..#{@max_level}`
* `:only` is a term or list of terms or a `check function`. If it is a term
or list of terms, the `function` is only called if the `key` of the
map is equal to the term or in the list of terms. If `:only` is a
`check function` then the `check function` is passed the `{k, v}` of
the current branch in the `map`. It is expected to return a `truthy`
value that if `true` signals that the argument `function` will be executed.
* `:except` is a term or list of terms or a `check function`. If it is a term
or list of terms, the `function` is only called if the `key` of the
map is not equal to the term or not in the list of terms. If `:except` is a
`check function` then the `check function` is passed the `{k, v}` of
the current branch in the `map`. It is expected to return a `truthy`
value that if `true` signals that the argument `function` will not be executed.
* `:filter` is a term or list of terms or a `check function`. If the
`key` currently being processed equals the term (or is in the list of
terms, or the `check_function` returns a truthy value) then this branch
of the map is processed by `function` and its output is included in the result.
* `:reject` is a term or list of terms or a `check function`. If the
`key` currently being processed equals the term (or is in the list of
terms, or the `check_function` returns a truthy value) then this branch
of the map is ommited from the mapped output.
* `:skip` is a term or list of terms or a `check function`. If the
`key` currently being processed equals the term (or is in the list of
terms, or the `check_function` returns a truthy value) then this branch
of the map is *not* processed by `function` but it is included in
the mapped result.
## Notes
* `:only` and `:except` operate on individual keys whereas `:filter`
and `:filter` and `:reject` operator on *entire branches* of a map
* If both the options `:only` and `:except` are provided then the `function`
is called only when a `term` meets both criteria. That means that `:except`
has priority over `:only`.
* If both the options `:filter` and `:reject` are provided then `:reject`
has priority over `:filter`.
## Returns
* The `map` transformed by the recursive application of
`function`
## Examples
iex> map = %{a: :a, b: %{c: :c}}
iex> fun = fn
...> {k, v} when is_atom(k) -> {Atom.to_string(k), v}
...> other -> other
...> end
iex> Cldr.Map.deep_map map, fun
%{"a" => :a, "b" => %{"c" => :c}}
iex> map = %{a: :a, b: %{c: :c}}
iex> Cldr.Map.deep_map map, fun, only: :c
%{a: :a, b: %{"c" => :c}}
iex> Cldr.Map.deep_map map, fun, except: [:a, :b]
%{a: :a, b: %{"c" => :c}}
iex> Cldr.Map.deep_map map, fun, level: 2
%{a: :a, b: %{"c" => :c}}
"""
@spec deep_map(
map() | list(),
function :: function() | {function(), function()},
options :: list()
) ::
map() | list()
def deep_map(map, function, options \\ @default_deep_map_options)
# Don't deep map structs since they have atom keys anyway and they
# also don't support enumerable
def deep_map(%_struct{} = map, _function, _options) when is_map(map) do
map
end
def deep_map(map_or_list, function, options)
when (is_map(map_or_list) or is_list(map_or_list)) and is_list(options) do
options = validate_options(function, options)
deep_map(map_or_list, function, options, @starting_level)
end
defp deep_map(nil, _fun, _options, _level) do
nil
end
# If the level is greater than the return
# just return the map or list
defp deep_map(map_or_list, _function, %{level: %{last: last}}, level) when level > last do
map_or_list
end
# If the level is less than the range then keep recursing
# without executing the function
defp deep_map(map, function, %{level: %{first: first}} = options, level)
when is_map(map) and level < first do
Enum.map(map, fn
{k, v} when is_map(v) or is_list(v) ->
{k, deep_map(v, function, options, level + 1)}
{k, v} ->
{k, v}
end)
|> Map.new()
end
# Here we are in range so we conditionally execute the function
# if the options for `:only` and `:except` are matched
defp deep_map(map, function, options, level) when is_map(map) and is_function(function) do
Enum.reduce(map, [], fn
{k, v}, acc when is_map(v) or is_list(v) ->
case process_type({k, v}, options) do
:process ->
v = deep_map(v, function, options, level + 1)
[function.({k, v}) | acc]
:except ->
v = deep_map(v, function, options, level + 1)
[{k, v} | acc]
:skip ->
[function.({k, v}) | acc]
:reject ->
acc
end
{k, v}, acc ->
case process_type({k, v}, options) do
:process ->
[function.({k, v}) | acc]
:except ->
[{k, v} | acc]
:skip ->
[function.({k, v}) | acc]
:reject ->
acc
end
end)
|> Map.new()
end
defp deep_map(map, {key_function, value_function}, options, level) when is_map(map) do
Enum.reduce(map, [], fn
{k, v}, acc when is_map(v) or is_list(v) ->
case process_type({k, v}, options) do
:process ->
v = deep_map(v, {key_function, value_function}, options, level + 1)
[{key_function.(k), value_function.(v)} | acc]
:except ->
v = deep_map(v, {key_function, value_function}, options, level + 1)
[{k, v} | acc]
:skip ->
[{key_function.(k), v} | acc]
:reject ->
acc
end
{k, v}, acc ->
case process_type({k, v}, options) do
:process ->
[{key_function.(k), value_function.(v)} | acc]
:except ->
[{k, v} | acc]
:skip ->
[{key_function.(k), v} | acc]
:reject ->
acc
end
end)
|> Map.new()
end
defp deep_map([], _function, _options, _level) do
[]
end
defp deep_map([head | rest], function, options, level) do
case process_type(head, options) do
:process ->
[deep_map(head, function, options, level + 1) | deep_map(rest, function, options, level + 1)]
:except ->
[deep_map(head, function, options, level + 1) | deep_map(rest, function, options, level + 1)]
:skip ->
[head | deep_map(rest, function, options, level + 1)]
:reject ->
deep_map(rest, function, options, level + 1)
end
end
defp deep_map(value, function, options, _level) when is_function(function) do
case process_type(value, options) do
:process ->
function.(value)
:except ->
value
:skip ->
value
:reject ->
nil
end
end
defp deep_map(value, {_key_function, value_function}, options, _level) do
case process_type(value, options) do
:process ->
value_function.(value)
:skip ->
value
:reject ->
nil
end
end
@doc """
Transforms a `map`'s `String.t` keys to `atom()` keys.
## Arguments
* `map` is any `t:map/0`
* `options` is a keyword list of options passed
to `deep_map/3`. One additional option apples
to this function directly:
* `:only_existing` which is set to `true` will
only convert the binary value to an atom if the atom
already exists. The default is `false`.
## Example
iex> Cldr.Map.atomize_keys %{"a" => %{"b" => %{1 => "c"}}}
%{a: %{b: %{1 => "c"}}}
"""
@default_atomize_options [only_existing: false]
def atomize_keys(map, options \\ [])
def atomize_keys(map, options) when is_map(map) or is_list(map) do
options = @default_atomize_options ++ options
deep_map(map, &atomize_key(&1, Map.new(options)), options)
end
def atomize_keys({k, value}, options) when is_map(value) or is_list(value) do
options = @default_atomize_options ++ options
map_options = Map.new(options)
{atomize_key(k, map_options), deep_map(value, &atomize_key(&1, map_options), options)}
end
# For compatibility with older
# versions of ex_cldr that expect this
# behaviour
# TODO Fix Cldr.Config.get_locale to not make this assumption
def atomize_keys(other, _options) do
other
end
@doc """
Transforms a `map`'s `String.t` values to `atom()` values.
## Arguments
* `map` is any `t:map/0`
* `options` is a keyword list of options passed
to `deep_map/3`. One additional option apples
to this function directly:
* `:only_existing` which is set to `true` will
only convert the binary value to an atom if the atom
already exists. The default is `false`.
## Examples
iex> Cldr.Map.atomize_values %{"a" => %{"b" => %{1 => "c"}}}
%{"a" => %{"b" => %{1 => :c}}}
"""
def atomize_values(map, options \\ [only_existing: false])
def atomize_values(map, options) when is_map(map) or is_list(map) do
options = @default_atomize_options ++ options
deep_map(map, &atomize_value(&1, Map.new(options)), options)
end
def atomize_values({k, value}, options) when is_map(value) or is_list(value) do
options = @default_atomize_options ++ options
map_options = Map.new(options)
{k, deep_map(value, &atomize_value(&1, map_options), options)}
end
@doc """
Transforms a `map`'s `String.t` keys to `Integer.t` keys.
## Arguments
* `map` is any `t:map/0`
* `options` is a keyword list of options passed
to `deep_map/3`
The map key is converted to an `integer` from
either an `atom` or `String.t` only when the
key is comprised of `integer` digits.
Keys which cannot be converted to an `integer`
are returned unchanged.
## Example
iex> Cldr.Map.integerize_keys %{a: %{"1" => "value"}}
%{a: %{1 => "value"}}
"""
def integerize_keys(map, options \\ [])
def integerize_keys(map, options) when is_map(map) or is_list(map) do
deep_map(map, &integerize_key/1, options)
end
def integerize_keys({k, value}, options) when is_map(value) or is_list(value) do
{integerize_key(k), deep_map(value, &integerize_key/1, options)}
end
@doc """
Transforms a `map`'s `String.t` values to `Integer.t` values.
## Arguments
* `map` is any `t:map/0`
* `options` is a keyword list of options passed
to `deep_map/3`
The map value is converted to an `integer` from
either an `atom` or `String.t` only when the
value is comprised of `integer` digits.
Keys which cannot be converted to an integer
are returned unchanged.
## Example
iex> Cldr.Map.integerize_values %{a: %{b: "1"}}
%{a: %{b: 1}}
"""
def integerize_values(map, options \\ []) do
deep_map(map, &integerize_value/1, options)
end
@doc """
Transforms a `map`'s `String.t` keys to `Float.t` values.
## Arguments
* `map` is any `t:map/0`
* `options` is a keyword list of options passed
to `deep_map/3`
The map key is converted to a `float` from
a `String.t` only when the key is comprised of
a valid float form.
Keys which cannot be converted to a `float`
are returned unchanged.
## Examples
iex> Cldr.Map.floatize_keys %{a: %{"1.0" => "value"}}
%{a: %{1.0 => "value"}}
iex> Cldr.Map.floatize_keys %{a: %{"1" => "value"}}
%{a: %{1.0 => "value"}}
"""
def floatize_keys(map, options \\ [])
def floatize_keys(map, options) when is_map(map) or is_list(map) do
deep_map(map, &floatize_key/1, options)
end
def floatize_keys({k, value}, options) when is_map(value) or is_list(value) do
{floatize_key(k), deep_map(value, &floatize_key/1, options)}
end
@doc """
Transforms a `map`'s `String.t` values to `Float.t` values.
## Arguments
* `map` is any `t:map/0`
* `options` is a keyword list of options passed
to `deep_map/3`
The map value is converted to a `float` from
a `String.t` only when the
value is comprised of a valid float form.
Values which cannot be converted to a `float`
are returned unchanged.
## Examples
iex> Cldr.Map.floatize_values %{a: %{b: "1.0"}}
%{a: %{b: 1.0}}
iex> Cldr.Map.floatize_values %{a: %{b: "1"}}
%{a: %{b: 1.0}}
"""
def floatize_values(map, options \\ []) do
deep_map(map, &floatize_value/1, options)
end
@doc """
Transforms a `map`'s `atom()` keys to `String.t` keys.
## Arguments
* `map` is any `t:map/0`
* `options` is a keyword list of options passed
to `deep_map/3`
## Example
iex> Cldr.Map.stringify_keys %{a: %{"1" => "value"}}
%{"a" => %{"1" => "value"}}
"""
def stringify_keys(map, options \\ [])
def stringify_keys(map, options) when is_map(map) or is_list(map) do
deep_map(map, &stringify_key/1, options)
end
def stringify_keys({k, value}, options) when is_map(value) or is_list(value) do
{stringify_key(k), deep_map(value, &stringify_key/1, options)}
end
@doc """
Transforms a `map`'s `atom()` keys to `String.t` keys.
## Arguments
* `map` is any `t:map/0`
* `options` is a keyword list of options passed
to `deep_map/3`
## Example
iex> Cldr.Map.stringify_values %{a: %{"1" => :value}}
%{a: %{"1" => "value"}}
"""
def stringify_values(map, options \\ []) do
deep_map(map, &stringify_value/1, options)
end
@doc """
Convert map `String.t` keys from `camelCase` to `snake_case`
* `map` is any `t:map/0`
* `options` is a keyword list of options passed
to `deep_map/3`
## Example
iex> Cldr.Map.underscore_keys %{"a" => %{"thisOne" => "value"}}
%{"a" => %{"this_one" => "value"}}
"""
def underscore_keys(map, options \\ [])
def underscore_keys(map, options) when is_map(map) or is_nil(map) do
deep_map(map, &underscore_key/1, options)
end
def underscore_keys({k, value}, options) when is_map(value) or is_list(value) do
{underscore_key(k), deep_map(value, &underscore_key/1, options)}
end
@doc """
Rename map keys from `from` to `to`
* `map` is any `t:map/0`
* `from` is any value map key
* `to` is any valid map key
* `options` is a keyword list of options passed
to `deep_map/3`
## Example
iex> Cldr.Map.rename_keys %{"a" => %{"this_one" => "value"}}, "this_one", "that_one"
%{"a" => %{"that_one" => "value"}}
"""
def rename_keys(map, from, to, options \\ []) when is_map(map) or is_list(map) do
renamer = fn
{^from, v} -> {to, v}
other -> other
end
deep_map(map, renamer, options)
end
@doc """
Convert a camelCase string or atome to a snake_case
* `string` is a `String.t` or `atom()` to be
transformed
This is the code of Macro.underscore with modifications.
The change is to cater for strings in the format:
This_That
which in Macro.underscore gets formatted as
this__that (note the double underscore)
when we actually want
that_that
## Examples
iex> Cldr.Map.underscore "thisThat"
"this_that"
iex> Cldr.Map.underscore "This_That"
"this_that"
"""
@spec underscore(string :: String.t() | atom()) :: String.t()
def underscore(<<h, t::binary>>) do
<<to_lower_char(h)>> <> do_underscore(t, h)
end
def underscore(other) do
other
end
# h is upper case, next char is not uppercase, or a _ or . => and prev != _
defp do_underscore(<<h, t, rest::binary>>, prev)
when h >= ?A and h <= ?Z and not (t >= ?A and t <= ?Z) and t != ?. and t != ?_ and t != ?- and
prev != ?_ do
<<?_, to_lower_char(h), t>> <> do_underscore(rest, t)
end
# h is uppercase, previous was not uppercase or _
defp do_underscore(<<h, t::binary>>, prev)
when h >= ?A and h <= ?Z and not (prev >= ?A and prev <= ?Z) and prev != ?_ do
<<?_, to_lower_char(h)>> <> do_underscore(t, h)
end
# h is dash "-" -> replace with underscore "_"
defp do_underscore(<<?-, t::binary>>, _) do
<<?_>> <> underscore(t)
end
# h is .
defp do_underscore(<<?., t::binary>>, _) do
<<?/>> <> underscore(t)
end
# Any other char
defp do_underscore(<<h, t::binary>>, _) do
<<to_lower_char(h)>> <> do_underscore(t, h)
end
defp do_underscore(<<>>, _) do
<<>>
end
defp to_lower_char(char) when char == ?-, do: ?_
defp to_lower_char(char) when char >= ?A and char <= ?Z, do: char + 32
defp to_lower_char(char), do: char
@doc """
Removes any leading underscores from `map`
`String.t` keys.
* `map` is any `t:map/0`
* `options` is a keyword list of options passed
to `deep_map/3`
## Examples
iex> Cldr.Map.remove_leading_underscores %{"a" => %{"_b" => "b"}}
%{"a" => %{"b" => "b"}}
"""
def remove_leading_underscores(map, options \\ []) do
remover = fn
{k, v} when is_binary(k) -> {String.trim_leading(k, "_"), v}
other -> other
end
deep_map(map, remover, options)
end
@doc """
Returns the result of deep merging a list of maps
## Examples
iex> Cldr.Map.merge_map_list [%{a: "a", b: "b"}, %{c: "c", d: "d"}]
%{a: "a", b: "b", c: "c", d: "d"}
"""
def merge_map_list([h | []]) do
h
end
def merge_map_list([h | t]) do
deep_merge(h, merge_map_list(t))
end
def merge_map_list([]) do
[]
end
@doc """
Deep merge two maps
* `left` is any `t:map/0`
* `right` is any `t:map/0`
## Examples
iex> Cldr.Map.deep_merge %{a: "a", b: "b"}, %{c: "c", d: "d"}
%{a: "a", b: "b", c: "c", d: "d"}
iex> Cldr.Map.deep_merge %{a: "a", b: "b"}, %{c: "c", d: "d", a: "aa"}
%{a: "aa", b: "b", c: "c", d: "d"}
"""
def deep_merge(left, right) when is_map(left) and is_map(right) do
Map.merge(left, right, &deep_resolve/3)
end
# Key exists in both maps, and both values are maps as well.
# These can be merged recursively.
defp deep_resolve(_key, left, right) when is_map(left) and is_map(right) do
deep_merge(left, right)
end
# Key exists in both maps, but at least one of the values is
# NOT a map. We fall back to standard merge behavior, preferring
# the value on the right.
defp deep_resolve(_key, _left, right) do
right
end
@doc """
Delete all members of a map that have a
key in the list of keys
## Examples
iex> Cldr.Map.delete_in %{a: "a", b: "b"}, [:a]
%{b: "b"}
"""
def delete_in(%{} = map, keys) when is_list(keys) do
Enum.reject(map, fn {k, _v} -> k in keys end)
|> Enum.map(fn {k, v} -> {k, delete_in(v, keys)} end)
|> Map.new()
end
def delete_in(map, keys) when is_list(map) and is_binary(keys) do
delete_in(map, [keys])
end
def delete_in(map, keys) when is_list(map) do
Enum.reject(map, fn {k, _v} -> k in keys end)
|> Enum.map(fn {k, v} -> {k, delete_in(v, keys)} end)
end
def delete_in(%{} = map, keys) when is_binary(keys) do
delete_in(map, [keys])
end
def delete_in(other, _keys) do
other
end
def from_keyword([] = list) do
Map.new(list)
end
def from_keyword([{key, _value} | _rest] = keyword_list) when is_atom(key) do
Map.new(keyword_list)
end
#
# Helpers
#
defp validate_options(function, options) when is_function(function) do
validate_options(options)
end
defp validate_options({key_function, value_function}, options)
when is_function(key_function) and is_function(value_function) do
validate_options(options)
end
defp validate_options(function, _options) do
raise ArgumentError,
"function parameter must be a function or a 2-tuple " <>
"consisting of a key_function and a value_function. Found #{inspect(function)}"
end
defp validate_options(options) do
@default_deep_map_options
|> Keyword.merge(options)
|> Map.new()
|> Map.update!(:level, fn
level when is_integer(level) ->
level..level
%Range{} = level ->
level
other ->
raise ArgumentError, ":level must be an integer or a range. Found #{inspect(other)}"
end)
end
defp atomize_key({k, v}, %{only_existing: true}) when is_binary(k) do
{String.to_existing_atom(k), v}
rescue
ArgumentError -> {k, v}
end
defp atomize_key({k, v}, %{only_existing: false}) when is_binary(k) do
{String.to_atom(k), v}
end
defp atomize_key(other, _options) do
other
end
defp atomize_value({k, v}, %{only_existing: true}) when is_binary(v) do
{k, String.to_existing_atom(v)}
rescue
ArgumentError -> {k, v}
end
defp atomize_value({k, v}, %{only_existing: false}) when is_binary(v) do
{k, String.to_atom(v)}
end
defp atomize_value(v, %{only_existing: false}) when is_binary(v) do
String.to_atom(v)
end
defp atomize_value(other, _options) do
other
end
defp integerize_key({k, v}) when is_binary(k) do
case Integer.parse(k) do
{integer, ""} -> {integer, v}
_other -> {k, v}
end
end
defp integerize_key(other) do
other
end
defp integerize_value({k, v}) when is_binary(v) do
case Integer.parse(v) do
{integer, ""} -> {k, integer}
_other -> {k, v}
end
end
defp integerize_value(other) do
other
end
defp floatize_key({k, v}) when is_binary(k) do
case Float.parse(k) do
{float, ""} -> {float, v}
_other -> {k, v}
end
end
defp floatize_key(other) do
other
end
defp floatize_value({k, v}) when is_binary(v) do
case Float.parse(v) do
{float, ""} -> {k, float}
_other -> {k, v}
end
end
defp floatize_value(other) do
other
end
defp stringify_key({k, v}) when is_atom(k), do: {Atom.to_string(k), v}
defp stringify_key(other), do: other
defp stringify_value({k, v}) when is_atom(v), do: {k, Atom.to_string(v)}
defp stringify_value(other) when is_atom(other), do: Atom.to_string(other)
defp stringify_value(other), do: other
defp underscore_key({k, v}) when is_binary(k), do: {underscore(k), v}
defp underscore_key(other), do: other
# process_element?/2 determines whether the
# calling function should apply to a given
# value
def process_type(x, options) do
filter? = filter?(x, options)
reject? = reject?(x, options)
cond do
reject? -> :reject
skip?(x, options) -> :skip
filter? && only?(x, options) && !except?(x, options) -> :process
filter? -> :except
end
# |> IO.inspect(label: inspect(x))
end
# Keep this branch but don't process it
defp skip?(x, %{skip: skip}) when is_function(skip) do
skip.(x)
end
defp skip?({k, _v}, %{skip: skip}) when is_list(skip) do
k in skip
end
defp skip?({k, _v}, %{skip: skip}) do
k == skip
end
defp skip?(k, %{skip: skip}) when is_list(skip) do
k in skip
end
defp skip?(k, %{skip: skip}) do
k == skip
end
# Keep this branch is the result
defp filter?(x, %{filter: filter}) when is_function(filter) do
filter.(x)
end
defp filter?(_x, %{filter: []}) do
true
end
defp filter?({k, _v}, %{filter: filter}) when is_list(filter) do
k in filter
end
defp filter?({k, _v}, %{filter: filter}) do
k == filter
end
defp filter?(k, %{filter: filter}) when is_list(filter) do
k in filter
end
defp filter?(k, %{filter: filter}) do
k == filter
end
# Don't include this branch is the result
defp reject?(x, %{reject: reject}) when is_function(reject) do
reject.(x)
end
defp reject?({k, _v}, %{reject: reject}) when is_list(reject) do
k in reject
end
defp reject?({k, _v}, %{reject: reject}) do
k == reject
end
defp reject?(k, %{reject: reject}) when is_list(reject) do
k in reject
end
defp reject?(k, %{reject: reject}) do
k == reject
end
# Process this item
defp only?(x, %{only: only}) when is_function(only) do
only.(x)
end
defp only?(_x, %{only: []}) do
true
end
defp only?({k, _v}, %{only: only}) when is_list(only) do
k in only
end
defp only?({k, _v}, %{only: only}) do
k == only
end
defp only?(k, %{only: only}) when is_list(only) do
k in only
end
defp only?(k, %{only: only}) do
k == only
end
# Don;t process this item
defp except?(x, %{except: except}) when is_function(except) do
except.(x)
end
defp except?({k, _v}, %{except: except}) when is_list(except) do
k in except
end
defp except?({k, _v}, %{except: except}) do
k == except
end
defp except?(k, %{except: except}) when is_list(except) do
k in except
end
defp except?(k, %{except: except}) do
k == except
end
end