# `Localize.Utils.Map`
[🔗](https://github.com/elixir-localize/localize/blob/v0.39.0/lib/localize/utils/map.ex#L1)

Functions for transforming maps, keys and values.

This module provides recursive map traversal and transformation utilities
including key/value type conversion (atomize, stringify, integerize, floatize),
deep merging, key renaming, and `camelCase` to `snake_case` conversion.

The primary public API includes:

* `deep_map/3` - Recursively traverse and transform a map.

* `atomize_keys/2` and `atomize_values/2` - Convert string keys/values to atoms.

* `stringify_keys/2` and `stringify_values/2` - Convert atom keys/values to strings.

* `integerize_keys/2` and `integerize_values/2` - Convert string keys/values to integers.

* `floatize_keys/2` and `floatize_values/2` - Convert string keys/values to floats.

* `underscore_keys/2` - Convert camelCase string keys to snake_case.

* `rename_keys/4` - Rename specific map keys.

* `deep_merge/3` - Deep merge two maps.

* `merge_map_list/2` - Deep merge a list of maps.

* `delete_in/2` - Delete map members by key list.

* `invert/2` - Invert a map's keys and values.

* `underscore/1` - Convert a camelCase string to snake_case.

# `atomize_keys`

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 applies
  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`.

### Returns

* A map with string keys converted to atom keys.

### Examples

    iex> Localize.Utils.Map.atomize_keys %{"a" => %{"b" => %{1 => "c"}}}
    %{a: %{b: %{1 => "c"}}}

# `atomize_values`

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 applies
  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`.

### Returns

* A map with string values converted to atom values.

### Examples

    iex> Localize.Utils.Map.atomize_values %{"a" => %{"b" => %{1 => "c"}}}
    %{"a" => %{"b" => %{1 => :c}}}

# `deep_map`

```elixir
@spec deep_map(
  map() | list(),
  function :: function() | {function(), function()},
  options :: list()
) :: map() | list()
```

Recursively traverse a map and invoke a function
that transforms the map for 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
  `[level: 1..1000000, filter: [], reject: [], skip: [], only: [], except: []]`.

### 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..1000000`.

* `: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 omitted 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 `:reject` operate 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> Localize.Utils.Map.deep_map map, fun
    %{"a" => :a, "b" => %{"c" => :c}}
    iex> map = %{a: :a, b: %{c: :c}}
    iex> Localize.Utils.Map.deep_map map, fun, only: :c
    %{a: :a, b: %{"c" => :c}}
    iex> Localize.Utils.Map.deep_map map, fun, except: [:a, :b]
    %{a: :a, b: %{"c" => :c}}
    iex> Localize.Utils.Map.deep_map map, fun, level: 2
    %{a: :a, b: %{"c" => :c}}

# `deep_merge`

Deep merge two maps.

### Arguments

* `left` is any `t:map/0`.

* `right` is any `t:map/0`.

* `resolver` is a 3-arity function used to resolve merge conflicts.
  The default resolver prefers the right-side value and recursively
  merges nested maps.

### Returns

* A single map that is the deep merge of `left` and `right`.

### Examples

    iex> Localize.Utils.Map.deep_merge %{a: "a", b: "b"}, %{c: "c", d: "d"}
    %{a: "a", b: "b", c: "c", d: "d"}

    iex> Localize.Utils.Map.deep_merge %{a: "a", b: "b"}, %{c: "c", d: "d", a: "aa"}
    %{a: "aa", b: "b", c: "c", d: "d"}

# `delete_in`

Delete all members of a map that have a key in the list of keys.

### Arguments

* `map` is any `t:map/0` or keyword list.

* `keys` is a list of keys to delete.

### Returns

* The map with all matching keys removed at any depth.

### Examples

    iex> Localize.Utils.Map.delete_in %{a: "a", b: "b"}, [:a]
    %{b: "b"}

# `extract_strings`

Extract strings from a map or list.

Recursively process the map or list
and extract string values from maps
and string elements from lists.

### Arguments

* `map_or_list` is any `t:map/0` or list.

* `options` is a keyword list of options (currently unused).

### Returns

* A flattened list of all string values found in the map or list.

# `floatize_keys`

Transforms a `map`'s `String.t` keys to `Float.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 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.

### Returns

* A map with string keys converted to float keys where possible.

### Examples

    iex> Localize.Utils.Map.floatize_keys %{a: %{"1.0" => "value"}}
    %{a: %{1.0 => "value"}}

    iex> Localize.Utils.Map.floatize_keys %{a: %{"1" => "value"}}
    %{a: %{1.0 => "value"}}

# `floatize_values`

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.

### Returns

* A map with string values converted to float values where possible.

### Examples

    iex> Localize.Utils.Map.floatize_values %{a: %{b: "1.0"}}
    %{a: %{b: 1.0}}

    iex> Localize.Utils.Map.floatize_values %{a: %{b: "1"}}
    %{a: %{b: 1.0}}

# `identity`

Returns an argument unchanged.

Useful when a `noop` function is required.

### Returns

* The argument unchanged.

### Examples

    iex> Localize.Utils.Map.identity(:anything)
    :anything

# `integerize_keys`

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.

### Returns

* A map with string keys converted to integer keys where possible.

### Examples

    iex> Localize.Utils.Map.integerize_keys %{a: %{"1" => "value"}}
    %{a: %{1 => "value"}}

# `integerize_values`

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.

Values which cannot be converted to an integer
are returned unchanged.

### Returns

* A map with string values converted to integer values where possible.

### Examples

    iex> Localize.Utils.Map.integerize_values %{a: %{b: "1"}}
    %{a: %{b: 1}}

# `invert`

Invert a map.

Requires that the map is a simple map of
keys and a list of values or a single
non-map value.

### Arguments

* `map` is any `t:map/0`.

* `options` is a keyword list of options.

### Options

* `:duplicates` which determines how duplicate values
  are handled:

  * `nil` or `false` which is the default and means only
    one value is kept. `Map.new/1` is used meaning the
    selected value is non-deterministic.

  * `:keep` meaning duplicate values are returned in a list.

  * `:shortest` means the shortest duplicate is kept.
    This operates on string or atom values.

  * `:longest` means the longest duplicate is kept.
    This operates on string or atom values.

### Returns

* A map with keys and values swapped.

# `merge_map_list`

Returns the result of deep merging a list of maps.

### Arguments

* `list` is a list of maps to be merged.

* `resolver` is a 3-arity function used to resolve merge conflicts.
  The default resolver prefers the right-side value and recursively
  merges nested maps.

### Returns

* A single map that is the result of deep merging all maps in the list.

### Examples

    iex> Localize.Utils.Map.merge_map_list [%{a: "a", b: "b"}, %{c: "c", d: "d"}]
    %{a: "a", b: "b", c: "c", d: "d"}

# `prune`

Prune a potentially deeply nested map of some of its branches.

### Arguments

* `map` is any `t:map/0`.

* `fun` is a 1-arity function. Branches for which the function
  returns a truthy value are removed from the map.

### Returns

* The map with matching branches pruned.

# `remove_leading_underscores`

Removes any leading underscores from `map` `String.t` keys.

### Arguments

* `map` is any `t:map/0`.

* `options` is a keyword list of options passed
  to `deep_map/3`.

### Returns

* A map with leading underscores removed from string keys.

### Examples

    iex> Localize.Utils.Map.remove_leading_underscores %{"a" => %{"_b" => "b"}}
    %{"a" => %{"b" => "b"}}

# `rename_keys`

Rename map keys from `from` to `to`.

### Arguments

* `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`.

### Returns

* A map with keys matching `from` renamed to `to`.

### Examples

    iex> Localize.Utils.Map.rename_keys %{"a" => %{"this_one" => "value"}}, "this_one", "that_one"
    %{"a" => %{"that_one" => "value"}}

# `stringify_keys`

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`.

### Returns

* A map with atom keys converted to string keys.

### Examples

    iex> Localize.Utils.Map.stringify_keys %{a: %{"1" => "value"}}
    %{"a" => %{"1" => "value"}}

# `stringify_values`

Transforms a `map`'s `atom()` values to `String.t` values.

### Arguments

* `map` is any `t:map/0`.

* `options` is a keyword list of options passed
  to `deep_map/3`.

### Returns

* A map with atom values converted to string values.

### Examples

    iex> Localize.Utils.Map.stringify_values %{a: %{"1" => :value}}
    %{a: %{"1" => "value"}}

# `underscore`

```elixir
@spec underscore(string :: String.t() | atom()) :: String.t()
```

Convert a camelCase string or atom to snake_case.

This is the code of `Macro.underscore/1` with modifications.
The change is to cater for strings in the format `This_That`
which in `Macro.underscore/1` gets formatted as `this__that`
(note the double underscore) when we actually want `this_that`.

### Arguments

* `string` is a `String.t` or `atom()` to be transformed.

### Returns

* A snake_case `String.t`.

### Examples

    iex> Localize.Utils.Map.underscore "thisThat"
    "this_that"

    iex> Localize.Utils.Map.underscore "This_That"
    "this_that"

# `underscore_keys`

Convert map `String.t` keys from `camelCase` to `snake_case`.

### Arguments

* `map` is any `t:map/0`.

* `options` is a keyword list of options passed
  to `deep_map/3`.

### Returns

* A map with camelCase string keys converted to snake_case.

### Examples

    iex> Localize.Utils.Map.underscore_keys %{"a" => %{"thisOne" => "value"}}
    %{"a" => %{"this_one" => "value"}}

---

*Consult [api-reference.md](api-reference.md) for complete listing*
