Moar.Map (Moar v3.2.0)

View Source

Map-related functions.

Summary

Functions

Converts key in map to an atom, optionally transforming the value with value_transformer.

Converts keys in map to atoms.

Removes keys from a map where the value is nil.

Removes keys from a map where the value is blank.

Converts keys to atoms, traversing through descendant lists and maps.

Recursively converts structs to maps, including nested structs and lists of structs. This is particularly useful for converting structs to plain maps for JSON serialization.

Deeply merges two maps into a single map. (It will also accept keyword lists and convert them to maps, as long as they are not empty lists.)

Converts keys to strings, traversing through descendant lists and maps.

Takes keys from nested maps.

Converts a list of maps into a map of maps indexed by the values of one of the map keys. It's a map-specific version of the more generic Moar.Enum.index_by/2.

Merges two enumerables into a single map. Supports nil values for either enumerable.

Merges two maps, retaining any existing non-blank values.

Puts a key/value pair into the given map if the key is not alredy in the map, or if the value in the map is blank as defined by Moar.Term.blank?/1.

Like Map.put_new/3 but raises if key already exists in map.

Returns a copy of map with old_key_name changed to new_key_name.

Returns a copy of map with old_key_name changed to new_key_name.

Like rename_key/2 but raises if key is not in map

Like rename_key/3 but raises if key is not in map

Returns a copy of map after changing key names supplied by keys_map.

Like rename_keys/2 but raises if any key in keys_map is not in map.

Converts keys in map to strings.

Transforms values of map using transformer function.

Validates that the keys of map are equal to or a subset of valid_keys. In addition to being a list, valid_keys can be a map or struct, in which case the keys of the map or struct are used as the list of valid keys.

Functions

atomize_key(map, key, value_transformer \\ &Function.identity/1)

@spec atomize_key(map(), binary() | atom(), (any() -> any()) | nil) :: map()

Converts key in map to an atom, optionally transforming the value with value_transformer.

Raises if key is a string and map already has an atomized version of that key.

iex> Moar.Map.atomize_key(%{"number-one" => "one", "number-two" => "two"}, "number-one")
%{:number_one => "one", "number-two" => "two"}

iex> Moar.Map.atomize_key(%{"number-one" => "one", "number-two" => "two"}, "number-one", &String.upcase/1)
%{:number_one => "ONE", "number-two" => "two"}

atomize_key!(map, key, value_fn \\ &Function.identity/1)

@spec atomize_key!(map(), binary() | atom(), (any() -> any()) | nil) :: map()

Like atomize_key/3 but raises if key is not in map.

atomize_keys(map)

@spec atomize_keys(map()) :: map()

Converts keys in map to atoms.

Raises if converting a key from a string to an atom would result in a key conflict.

iex> Moar.Map.atomize_keys(%{"a" => 1, "b" => 2})
%{a: 1, b: 2}

compact(map)

@spec compact(map()) :: map()

Removes keys from a map where the value is nil.

iex> Moar.Map.compact(%{a: 1, b: nil, c: ""})
%{a: 1, c: ""}

compact_blank(map)

@spec compact_blank(map()) :: map()

Removes keys from a map where the value is blank.

iex> Moar.Map.compact_blank(%{a: 1, b: nil, c: "", d: []})
%{a: 1}

deep_atomize_keys(list)

@spec deep_atomize_keys(list() | map()) :: map()

Converts keys to atoms, traversing through descendant lists and maps.

Raises if converting a key from a string to an atom would result in a key conflict.

iex> Moar.Map.deep_atomize_keys(%{"a" => %{"aa" => 1}, "b" => [%{"bb" => 2}, %{"bbb" => 3}]})
%{a: %{aa: 1}, b: [%{bb: 2}, %{bbb: 3}]}

deep_from_struct(struct)

@spec deep_from_struct(struct() | map() | [struct() | map()]) :: map() | [map()]

Recursively converts structs to maps, including nested structs and lists of structs. This is particularly useful for converting structs to plain maps for JSON serialization.

deep_merge(a, b, conflict_fn \\ fn _val1, val2 -> val2 end)

@spec deep_merge(map() | keyword(), map() | keyword(), (any(), any() -> any())) ::
  map()

Deeply merges two maps into a single map. (It will also accept keyword lists and convert them to maps, as long as they are not empty lists.)

Optionally accepts conflict_fn which gets called when both enumerables have values at the same keypath. It receives the conflicting values from each map and is expected to return the winning value.

iex> Moar.Map.deep_merge(%{fruit: %{apples: 3, bananas: 5}, veggies: %{carrots: 10}}, [fruit: [cherries: 20]])
%{fruit: %{apples: 3, bananas: 5, cherries: 20}, veggies: %{carrots: 10}}

iex> Moar.Map.deep_merge(%{a: %{b: 1}}, %{a: %{b: 2}})
%{a: %{b: 2}}

iex> Moar.Map.deep_merge(%{a: %{b: 1}}, %{a: %{b: 2}}, fn x, _y -> x end)
%{a: %{b: 1}}

iex> Moar.Map.deep_merge(%{a: %{b: 1}}, %{a: %{b: 2}}, fn x, y -> [x, y] end)
%{a: %{b: [1, 2]}}

deep_stringify_keys(list)

@spec deep_stringify_keys(list() | map()) :: map()

Converts keys to strings, traversing through descendant lists and maps.

Raises if converting a key would result in a key conflict.

iex> Moar.Map.deep_stringify_keys(%{a: %{aa: 1}, b: [%{bb: 2}, %{bbb: 3}]})
%{"a" => %{"aa" => 1}, "b" => [%{"bb" => 2}, %{"bbb" => 3}]}

deep_take(map, keys)

@spec deep_take(
  map(),
  [atom() | binary() | {atom() | binary(), list() | map()}] | map()
) :: map()

Takes keys from nested maps.

iex> Moar.Map.deep_take(%{a: 1, b: %{c: 2, d: 3}, z: 9}, [:a, b: [:c]])
%{a: 1, b: %{c: 2}}

index_by(list_of_maps, key)

@spec index_by([map()], any()) :: map()

Converts a list of maps into a map of maps indexed by the values of one of the map keys. It's a map-specific version of the more generic Moar.Enum.index_by/2.

iex> Moar.Map.index_by([%{name: "Alice", tid: "alice"}, %{name: "Billy", tid: "billy"}], :tid)
%{"alice" => %{name: "Alice", tid: "alice"}, "billy" => %{name: "Billy", tid: "billy"}}

merge(a, b)

@spec merge(Enum.t() | nil, Enum.t() | nil) :: map()

Merges two enumerables into a single map. Supports nil values for either enumerable.

iex> Moar.Map.merge(%{a: 1}, [b: 2])
%{a: 1, b: 2}

iex> Moar.Map.merge(nil, [a: 1])
%{a: 1}

merge_if_blank(a, b)

@spec merge_if_blank(map(), map()) :: map()

Merges two maps, retaining any existing non-blank values.

iex> Moar.Map.merge_if_blank(%{a: 1, b: nil, c: ""}, %{a: 100, b: 2, c: 3, d: 4})
%{a: 1, b: 2, c: 3, d: 4}

put_if_blank(map, key, value)

@spec put_if_blank(map() | keyword(), any(), any()) :: map()

Puts a key/value pair into the given map if the key is not alredy in the map, or if the value in the map is blank as defined by Moar.Term.blank?/1.

Also, the map parameter can be any enumerable that can be turned into a map via Enum.into/2.

iex> %{a: 1} |> Moar.Map.put_if_blank(:b, 2)
%{a: 1, b: 2}

iex> %{a: 1, b: nil} |> Moar.Map.put_if_blank(:b, 2)
%{a: 1, b: 2}

iex> %{a: 1, b: 3} |> Moar.Map.put_if_blank(:b, 2)
%{a: 1, b: 3}

put_new!(map, key, value)

@spec put_new!(map(), any(), any()) :: map()

Like Map.put_new/3 but raises if key already exists in map.

iex> Moar.Map.put_new!(%{a: 1}, :b, 2)
%{a: 1, b: 2}

iex> Moar.Map.put_new!(%{a: 1, b: 2}, :b, 3)
** (RuntimeError) Map already contains key: :b

rename_key(map, old_and_new_key)

@spec rename_key(
  map(),
  {binary(), binary()}
) :: map()

Returns a copy of map with old_key_name changed to new_key_name.

old_key_name and new_key_name are passed in as a {old_key_name, new_key_name} tuple.

iex> %{"color" => "red", "size" => "medium"} |> Moar.Map.rename_key({"color", "colour"})
%{"colour" => "red", "size" => "medium"}

rename_key(map, old_key_name, new_key_name)

@spec rename_key(map(), binary() | atom(), binary() | atom()) :: map()

Returns a copy of map with old_key_name changed to new_key_name.

iex> %{"color" => "red", "size" => "medium"} |> Moar.Map.rename_key("color", "colour")
%{"colour" => "red", "size" => "medium"}

rename_key!(map, old_and_new_key)

@spec rename_key!(
  map(),
  {binary(), binary()}
) :: map()

Like rename_key/2 but raises if key is not in map

rename_key!(map, old_key_name, new_key_name)

@spec rename_key!(map(), binary() | atom(), binary() | atom()) :: map()

Like rename_key/3 but raises if key is not in map

rename_keys(map, keys_map)

@spec rename_keys(map(), map()) :: map()

Returns a copy of map after changing key names supplied by keys_map.

%{"behavior" => "chill", "color" => "red"} |> Moar.Map.rename_keys(%{"behavior" => "behaviour", "color" => "colour"})
%{"behaviour" => "chill", "colour" => "red"}

rename_keys!(map, keys_map)

@spec rename_keys!(map(), map()) :: map()

Like rename_keys/2 but raises if any key in keys_map is not in map.

stringify_keys(map)

@spec stringify_keys(map()) :: map()

Converts keys in map to strings.

iex> Moar.Map.stringify_keys(%{a: 1, b: 2} )
%{"a" => 1, "b" => 2}

transform(map, keys, transformer)

@spec transform(map(), atom() | binary() | list(), (any() -> any())) :: map()

Transforms values of map using transformer function.

iex> %{"foo" => "chicken", "bar" => "cow", "baz" => "pig"} |> Moar.Map.transform("foo", &String.upcase/1)
%{"foo" => "CHICKEN", "bar" => "cow", "baz" => "pig"}

iex> %{"foo" => "chicken", "bar" => "cow", "baz" => "pig"} |> Moar.Map.transform(["foo", "bar"], &String.upcase/1)
%{"foo" => "CHICKEN", "bar" => "COW", "baz" => "pig"}

validate_keys!(map, valid_keys)

@spec validate_keys!(map(), list() | map() | struct()) :: map()

Validates that the keys of map are equal to or a subset of valid_keys. In addition to being a list, valid_keys can be a map or struct, in which case the keys of the map or struct are used as the list of valid keys.

It returns the input map, or raises an exception if there are non-allowed keys.

iex> Moar.Map.validate_keys!(%{a: 1, b: 2}, [:a, :b, :c])
%{a: 1, b: 2}

iex> Moar.Map.validate_keys!(%{a: 1, b: 2}, %{a: nil, b: 2, c: :foo})
%{a: 1, b: 2}

iex> Moar.Map.validate_keys!(%{a: 1, b: 2}, [:a, :c])
** (ArgumentError) Non-allowed keys found in map: [:b]