Search documentation of Localize

# Localize.Utils.Map (Localize v0.39.0)

Copy Markdown View Source

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.

## Summary

### Functions

atomize_keys(map, options \\ [])

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

atomize_values(map, options \\ [only_existing: false])

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

deep_map(map, function, options \\ [level: %{__struct__: Range, first: 1, last: 1_000_000, step: 1}, filter: [], reject: [], skip: [], only: [], except: []])

Recursively traverse a map and invoke a function that transforms the map for each key/value pair.

deep_merge(left, right, resolver \\ &standard_deep_resolver/3)

Deep merge two maps.

delete_in(map, keys)

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

extract_strings(map_or_list, options \\ [])

Extract strings from a map or list.

floatize_keys(map, options \\ [])

Transforms a `map`'s `String.t` keys to `Float.t` keys.

floatize_values(map, options \\ [])

Transforms a `map`'s `String.t` values to `Float.t` values.

identity(x)

Returns an argument unchanged.

integerize_keys(map, options \\ [])

Transforms a `map`'s `String.t` keys to `Integer.t` keys.

integerize_values(map, options \\ [])

Transforms a `map`'s `String.t` values to `Integer.t` values.

invert(map, options \\ [])

Invert a map.

merge_map_list(list, resolver \\ &standard_deep_resolver/3)

Returns the result of deep merging a list of maps.

prune(map, fun)

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

remove_leading_underscores(map, options \\ [])

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

rename_keys(map, from, to, options \\ [])

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

stringify_keys(map, options \\ [])

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

stringify_values(map, options \\ [])

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

underscore(other)

Convert a camelCase string or atom to snake_case.

underscore_keys(map, options \\ [])

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

## Functions

# atomize_keys(map, options \\ [])

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

### Arguments

- `map` is any `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(map, options \\ [only_existing: false])

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

### Arguments

- `map` is any `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(map, function, options \\ [level: %{__struct__: Range, first: 1, last: 1_000_000, step: 1}, filter: [], reject: [], skip: [], only: [], except: []])

    @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 `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(left, right, resolver \\ &standard_deep_resolver/3)

Deep merge two maps.

### Arguments

- `left` is any `map/0`.

- `right` is any `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(map, keys)

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

### Arguments

- `map` is any `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(map_or_list, options \\ [])

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 `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(map, options \\ [])

Transforms a `map`'s `String.t` keys to `Float.t` keys.

### Arguments

- `map` is any `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(map, options \\ [])

Transforms a `map`'s `String.t` values to `Float.t` values.

### Arguments

- `map` is any `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(x)

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(map, options \\ [])

Transforms a `map`'s `String.t` keys to `Integer.t` keys.

### Arguments

- `map` is any `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(map, options \\ [])

Transforms a `map`'s `String.t` values to `Integer.t` values.

### Arguments

- `map` is any `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(map, options \\ [])

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 `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(list, resolver \\ &standard_deep_resolver/3)

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(map, fun)

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

### Arguments

- `map` is any `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(map, options \\ [])

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

### Arguments

- `map` is any `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(map, from, to, options \\ [])

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

### Arguments

- `map` is any `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(map, options \\ [])

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

### Arguments

- `map` is any `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(map, options \\ [])

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

### Arguments

- `map` is any `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(other)

    @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(map, options \\ [])

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

### Arguments

- `map` is any `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"}}