WuunderUtils.Maps (Wuunder Utils v0.1.1)

Contains a set of helpers to deal with some complex stuff with Maps and Structs

Summary

Functions

Maps a given field from given (if not in params)

Mass maps a given input with aliasses

Deletes a list of keys from a map (and all nested maps, lists) Usefull when you want to scrub out IDs for instance.

Removes a key from a map. Doesn't matter if the key is an atom or string

Flattens a map. This results in a map that just contains one level

Creates a clean map from a given struct. This function deep structs, maps, lists etc. to a map and uses a set of default transformers as defined in default_struct_fransform/0.

Retrieves a key from a map regardless of the key type (atom/string) Note that this function does not try to convert a given string key to an atom to prevent an atom overload.

Tests if the given map only consists of atom keys

Tests if the map or struct is present

Acts as an IndifferentMap. Put a key/value regardless of the key type. If the map contains keys as atoms, the value will be stored as atom: value. If the map contains strings as keys it will store the value as binary: value

Only puts value in map when value is actually nil (not the same as empty)

Only puts value in map when the value is considered empty

Conditionally puts a value to a given map. Depending on the condition or the value, the key+value will be set to the map

Types

@type map_key() :: atom() | binary()

Functions

Link to this function

alias_field(params, from, to)

@spec alias_field(map(), atom(), atom()) :: map()

Maps a given field from given (if not in params)

Examples

iex> WuunderUtils.Maps.alias_field(%{country: "NL"}, :country, :country_code)
%{country_code: "NL"}

iex> WuunderUtils.Maps.alias_field(%{"country" => "NL"}, :country, :country_code)
%{"country_code" => "NL"}

iex> WuunderUtils.Maps.alias_field(%{street_name: "Straatnaam"}, :street, :street_address)
%{street_name: "Straatnaam"}
Link to this function

alias_fields(params, aliasses)

@spec alias_fields(map(), map()) :: map()

Mass maps a given input with aliasses

Examples

iex> WuunderUtils.Maps.alias_fields(%{country: "NL", street: "Straat", number: 666}, %{country: :country_code, street: :street_name, number: :house_number})
%{country_code: "NL", house_number: 666, street_name: "Straat"}
Link to this function

delete_all(map, keys_to_delete)

@spec delete_all(map(), [String.t() | atom()]) :: map()

Deletes a list of keys from a map (and all nested maps, lists) Usefull when you want to scrub out IDs for instance.

Examples

iex> WuunderUtils.Maps.delete_all(
...>   %{
...>      shipment: %{
...>        id: "shipment-id",
...>        wuunder_id: "WUUNDERID"
...>      },
...>      order_lines: [
...>        %{id: "123", sku: "SKU01"},
...>        %{id: "456", sku: "SKU02"},
...>        %{id: "789", sku: "SKU03"}
...>      ],
...>      meta: %{
...>        configuration_id: "nothing"
...>      }
...>   },
...>   [:id, :configuration_id]
...> )
%{
  meta: %{},
  order_lines: [%{sku: "SKU01"}, %{sku: "SKU02"}, %{sku: "SKU03"}],
  shipment: %{wuunder_id: "WUUNDERID"}
}
Link to this function

delete_field(params, key)

@spec delete_field(map(), map_key()) :: map()

Removes a key from a map. Doesn't matter if the key is an atom or string

Examples

iex> WuunderUtils.Maps.delete_field(%{length: 255, weight: 100}, :length)
%{weight: 100}

iex> WuunderUtils.Maps.delete_field(%{length: 255, weight: 100}, "length")
%{weight: 100, length: 255}

iex> WuunderUtils.Maps.delete_field(%{"value" => 50, "currency" => "EUR"}, "currency")
%{"value" => 50}

iex> WuunderUtils.Maps.delete_field(%{"value" => 50, "currency" => "EUR"}, :currency)
%{"value" => 50}
Link to this function

flatten_map(map, options \\ [])

@spec flatten_map(map(), Keyword.t()) :: map()

Flattens a map. This results in a map that just contains one level

Options

  • key_separator (default .)
  • underscore_key (default true)
  • list_index_start (default 1)

Example

iex> WuunderUtils.Maps.flatten_map(%{
...>   test: "123",
...>   order_lines: [
...>     %{sku: "123", description: "test"},
...>     %{sku: "456", description: "test 2"}
...>   ],
...>   meta: %{
...>     data: "test"
...>   }
...> })
%{
  "test" => "123",
  "order_lines.1.sku" => "123",
  "order_lines.1.description" => "test",
  "order_lines.2.sku" => "456",
  "order_lines.2.description" => "test 2",
  "meta.data" => "test"
}

iex> WuunderUtils.Maps.flatten_map(
...>   %{
...>     test: "123",
...>     order_lines: [
...>       %{sku: "123", description: "test"},
...>       %{sku: "456", description: "test 2"}
...>     ],
...>     meta: %{
...>       data: "test"
...>     }
...>   },
...>   key_separator: "_",
...>   list_index_start: 0
...> )
%{
  "test" => "123",
  "order_lines_0_sku" => "123",
  "order_lines_0_description" => "test",
  "order_lines_1_sku" => "456",
  "order_lines_1_description" => "test 2",
  "meta_data" => "test"
}
Link to this function

flatten_map(map_or_list, initial_map, key_prefix, options)

@spec flatten_map(map() | list(), map(), String.t(), Keyword.t()) :: map()
Link to this function

from_struct(value)

@spec from_struct(any()) :: any()

Creates a clean map from a given struct. This function deep structs, maps, lists etc. to a map and uses a set of default transformers as defined in default_struct_fransform/0.

There is also an option to omit the transform option to add an extra set of transformers.

Took some inspiration from this great lib: https://github.com/prodis/miss-elixir/blob/0.1.5/lib/miss/map.ex

Note: It's also able to convert Ecto models to flat maps. It uses the defined Ecto fields for that.

Examples

iex> WuunderUtils.Maps.from_struct(%TestStruct{
...>   first_name: "Peter",
...>   last_name: "Pan",
...>   date_of_birth: ~D[1980-01-02],
...>   weight: Decimal.new("81.5"),
...>   country: %TestStruct2{code: "UK"},
...>   time_of_death: ~T[13:37:37]
...> })
%{
  address: nil,
  date_of_birth: "1980-01-02",
  first_name: "Peter",
  last_name: "Pan",
  time_of_death: "13:37:37",
  weight: "81.5",
  country: %{code: "UK"}
}

iex> WuunderUtils.Maps.from_struct(
...>   %TestStruct{
...>     first_name: "Peter",
...>     last_name: "Pan",
...>     date_of_birth: ~D[1980-01-02],
...>     weight: Decimal.new("81.5"),
...>     country: %TestStruct2{code: "UK"},
...>     time_of_death: ~T[13:37:37]
...>   },
...>   transform: [{TestStruct2, fn x -> "COUNTRY:" <> x.code end}]
...> )
%{
  address: nil,
  date_of_birth: "1980-01-02",
  first_name: "Peter",
  last_name: "Pan",
  time_of_death: "13:37:37",
  weight: "81.5",
  country: "COUNTRY:UK"
}

iex> WuunderUtils.Maps.from_struct(
...>   %TestStruct{
...>     address: %TestSchema{
...>       street: "Straat",
...>       number: 13,
...>       zipcode: "1122AB"
...>     },
...>     first_name: "Peter",
...>     last_name: "Pan",
...>     date_of_birth: ~D[1980-01-02],
...>     weight: Decimal.new("81.5"),
...>     country: %TestStruct2{code: "UK"},
...>     time_of_death: ~T[13:37:37]
...>   }
...> )
%{
  address: %{number: 13, street: "Straat", zipcode: "1122AB"},
  date_of_birth: "1980-01-02",
  first_name: "Peter",
  last_name: "Pan",
  time_of_death: "13:37:37",
  weight: "81.5",
  country: %{code: "UK"}
}
Link to this function

from_struct(value, transform)

@spec from_struct(any(), list()) :: any()
Link to this function

get_field(params, key, default \\ nil)

@spec get_field(map(), map_key(), any()) :: any()

Retrieves a key from a map regardless of the key type (atom/string) Note that this function does not try to convert a given string key to an atom to prevent an atom overload.

Examples

iex> WuunderUtils.Maps.get_field(%{value: 20}, :value)
20

iex> WuunderUtils.Maps.get_field(%{"value" => 20}, :value)
20

iex> WuunderUtils.Maps.get_field(%{value: 20}, "value")
nil

iex> WuunderUtils.Maps.get_field(%{value: 20}, "non-existent")
nil

iex> WuunderUtils.Maps.get_field(%{value: 20}, :weight)
nil

iex> WuunderUtils.Maps.get_field(%{value: 20}, :weight, 350)
350

iex> WuunderUtils.Maps.get_field(%{value: 20}, "currency", "EUR")
"EUR"
Link to this function

has_only_atom_keys?(struct)

@spec has_only_atom_keys?(map() | struct()) :: boolean()

Tests if the given map only consists of atom keys

Examples

iex> WuunderUtils.Maps.has_only_atom_keys?(%{a: 1, b: 2})
true

iex> WuunderUtils.Maps.has_only_atom_keys?(%{:a => 1, "b" => 2})
false

iex> WuunderUtils.Maps.has_only_atom_keys?(%{"a" => 1, "b" => 2})
false
Link to this macro

is_valid_map_atom_key(key)

(macro)
Link to this macro

is_valid_map_binary_key(key)

(macro)
Link to this macro

is_valid_map_key(key)

(macro)
Link to this function

present?(value)

@spec present?(Ecto.Association.NotLoaded.t() | nil | map()) :: boolean()

Tests if the map or struct is present

Examples

iex> WuunderUtils.Maps.present?(nil)
false

iex> WuunderUtils.Maps.present?(%{})
false

iex> WuunderUtils.Maps.present?(%{a: 1})
true

iex> WuunderUtils.Maps.present?(%TestStruct{})
true

iex> WuunderUtils.Maps.present?(%Ecto.Association.NotLoaded{})
false
Link to this function

put_field(params, key, value)

@spec put_field(map(), map_key(), any()) :: map()

Acts as an IndifferentMap. Put a key/value regardless of the key type. If the map contains keys as atoms, the value will be stored as atom: value. If the map contains strings as keys it will store the value as binary: value

Note that this will not try to convert the given string key to an atom if the map contains only atom keys (the same reason as stated in helper function get_field)

Examples

iex> WuunderUtils.Maps.put_field(%{value: 20}, :weight, 350)
%{value: 20, weight: 350}

iex> WuunderUtils.Maps.put_field(%{value: 20}, "weight", 350)
%{:value => 20, "weight" => 350}

iex> WuunderUtils.Maps.put_field(%{"weight" => 350}, :value, 25)
%{"weight" => 350, "value" => 25}

iex> WuunderUtils.Maps.put_field(%{"weight" => 350}, "value", 25)
%{"weight" => 350, "value" => 25}
Link to this function

put_if_not_nil(map, key, value)

@spec put_if_not_nil(map(), map_key(), any()) :: map()

Only puts value in map when value is actually nil (not the same as empty)

Examples

iex> WuunderUtils.Maps.put_if_not_nil(%{street: "Straat"}, :street, "Laan")
%{street: "Laan"}

iex> WuunderUtils.Maps.put_if_not_nil(%{street: "Straat"}, :street, nil)
%{street: "Straat"}

iex> WuunderUtils.Maps.put_if_not_nil(%{street: "Straat"}, :street, "     ")
%{street: "     "}
Link to this function

put_if_present(params, key, value)

@spec put_if_present(map(), map_key(), any()) :: map()

Only puts value in map when the value is considered empty

Examples

iex> WuunderUtils.Maps.put_if_present(%{street: "Straat"}, :street, "Laan")
%{street: "Laan"}

iex> WuunderUtils.Maps.put_if_present(%{street: "Straat"}, :street, nil)
%{street: "Straat"}

iex> WuunderUtils.Maps.put_if_present(%{street: "Straat"}, :street, "     ")
%{street: "Straat"}
Link to this function

put_when(params, condition, key, value)

@spec put_when(map(), function() | boolean(), map_key(), any()) :: map()

Conditionally puts a value to a given map. Depending on the condition or the value, the key+value will be set to the map

Examples

iex> WuunderUtils.Maps.put_when(%{street: "Straat"}, 1 == 1, :number, 13)
%{number: 13, street: "Straat"}

iex> WuunderUtils.Maps.put_when(%{street: "Straat"}, fn -> "value" == "value" end, :number, 13)
%{number: 13, street: "Straat"}

iex> WuunderUtils.Maps.put_when(%{street: "Straat"}, 10 > 20, :number, 13)
%{street: "Straat"}