Miss.Map (Miss Elixir v0.1.2) View Source

Functions to extend the Elixir Map module.

Link to this section Summary

Functions

Converts a struct to map going through all nested structs, different from Map.from_struct/1 that only converts the root struct.

Gets the value for a specific key in map.

Renames a single key in the given map.

Renames keys in the given map.

Link to this section Types

Specs

keys_to_rename() :: [{actual_key :: Map.key(), new_key :: Map.key()}] | map()

Specs

transform() :: [{module(), (module() -> term()) | :skip}]

Link to this section Functions

Link to this function

from_nested_struct(struct, transform \\ [])

View Source

Specs

from_nested_struct(struct(), transform()) :: map()

Converts a struct to map going through all nested structs, different from Map.from_struct/1 that only converts the root struct.

The optional parameter transform receives a list of tuples with the struct module and a function to be called instead of converting to a map. The transforming function will receive the struct as a single parameter.

If you want to skip the conversion of a nested struct, just pass the atom :skip instead of a transformation function.

Date or Decimal values are common examples where their map representation could be not so useful when converted to a map. See the examples for more details.

Examples

# Given the following structs

defmodule Post do
  defstruct [:title, :text, :date, :author, comments: []]
end

defmodule Author do
  defstruct [:name, :metadata]
end

defmodule Comment do
  defstruct [:text]
end

defmodule Metadata do
  defstruct [:atom, :boolean, :decimal, :float, :integer]
end

# Convert all nested structs including the Date and Decimal values:

iex> post = %Post{
...>   title: "My post",
...>   text: "Something really interesting",
...>   date: ~D[2010-09-01],
...>   author: %Author{
...>     name: "Pedro Bonamides",
...>     metadata: %Metadata{
...>       atom: :my_atom,
...>       boolean: true,
...>       decimal: Decimal.new("456.78"),
...>       float: 987.54,
...>       integer: 2_345_678
...>     }
...>   },
...>   comments: [
...>     %Comment{text: "Comment one"},
...>     %Comment{text: "Comment two"}
...>   ]
...> }
...> Miss.Map.from_nested_struct(post)
%{
  title: "My post",
  text: "Something really interesting",
  date: %{calendar: Calendar.ISO, day: 1, month: 9, year: 2010},
  author: %{
    name: "Pedro Bonamides",
    metadata: %{
      atom: :my_atom,
      boolean: true,
      decimal: %{coef: 45678, exp: -2, sign: 1},
      float: 987.54,
      integer: 2_345_678
    }
  },
  comments: [
    %{text: "Comment one"},
    %{text: "Comment two"}
  ]
}

# Convert all nested structs skipping the Date values and transforming Decimal values to string:

iex> post = %Post{
...>   title: "My post",
...>   text: "Something really interesting",
...>   date: ~D[2010-09-01],
...>   author: %Author{
...>     name: "Pedro Bonamides",
...>     metadata: %Metadata{
...>       atom: :my_atom,
...>       boolean: true,
...>       decimal: Decimal.new("456.78"),
...>       float: 987.54,
...>       integer: 2_345_678
...>     }
...>   },
...>   comments: [
...>     %Comment{text: "Comment one"},
...>     %Comment{text: "Comment two"}
...>   ]
...> }
...> Miss.Map.from_nested_struct(post, [{Date, :skip}, {Decimal, &to_string/1}])
%{
  title: "My post",
  text: "Something really interesting",
  date: ~D[2010-09-01],
  author: %{
    name: "Pedro Bonamides",
    metadata: %{
      atom: :my_atom,
      boolean: true,
      decimal: "456.78",
      float: 987.54,
      integer: 2_345_678
    }
  },
  comments: [
    %{text: "Comment one"},
    %{text: "Comment two"}
  ]
}

Specs

get!(map(), Map.key()) :: Map.value()

Gets the value for a specific key in map.

If key is present in map, the corresponding value is returned. Otherwise, a KeyError is raised.

Miss.Map.get!/2 is similar to Map.fetch!/2 but more efficient. Using pattern matching is the fastest way to access maps. Miss.Map.get!/2 uses pattern matching, but Map.fetch!/2 not.

Examples

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

iex> Miss.Map.get!(%{a: 1, b: 2}, :c)
** (KeyError) key :c not found in: %{a: 1, b: 2}
Link to this function

rename_key(map, actual_key, new_key)

View Source

Specs

rename_key(map(), Map.key(), Map.key()) :: map()

Renames a single key in the given map.

If actual_key does not exist in map, it is simply ignored.

If a key is renamed to an existing key, the value of the actual key remains.

Examples

iex> Miss.Map.rename_key(%{a: 1, b: 2, c: 3}, :b, :bbb)
%{a: 1, bbb: 2, c: 3}

iex> Miss.Map.rename_key(%{"a" => 1, "b" => 2, "c" => 3}, "b", "bbb")
%{"a" => 1, "bbb" => 2, "c" => 3}

iex> Miss.Map.rename_key(%{a: 1, b: 2, c: 3}, :z, :zzz)
%{a: 1, b: 2, c: 3}

iex> Miss.Map.rename_key(%{a: 1, b: 2, c: 3}, :a, :c)
%{b: 2, c: 1}

iex> Miss.Map.rename_key(%{a: 1, b: 2, c: 3}, :c, :a)
%{a: 3, b: 2}
Link to this function

rename_keys(map, keys_to_rename)

View Source

Specs

rename_keys(map(), keys_to_rename()) :: map()

Renames keys in the given map.

Keys to be renamed are given through keys_to_rename that accepts either:

  • a list of two-element tuples: {actual_key, new_key}; or
  • a map where the keys are the actual keys and the values are the new keys: %{actual_key => new_key}

If keys_to_rename contains keys that are not in map, they are simply ignored.

It is not recommended to use Miss.Map.rename_keys/2 to rename keys to existing keys. But if you do it, after renaming the keys, duplicate keys are removed and the value of the preceding one prevails. See the examples for more details.

Examples

iex> Miss.Map.rename_keys(%{a: 1, b: 2, c: 3}, %{a: :aaa, c: :ccc})
%{aaa: 1, b: 2, ccc: 3}

iex> Miss.Map.rename_keys(%{a: 1, b: 2, c: 3}, a: :aaa, c: :ccc)
%{aaa: 1, b: 2, ccc: 3}

iex> Miss.Map.rename_keys(%{"a" => 1, "b" => 2, "c" => 3}, %{"a" => "aaa", "b" => "bbb"})
%{"aaa" => 1, "bbb" => 2, "c" => 3}

iex> Miss.Map.rename_keys(%{"a" => 1, "b" => 2, "c" => 3}, [{"a", "aaa"}, {"b", "bbb"}])
%{"aaa" => 1, "bbb" => 2, "c" => 3}

iex> Miss.Map.rename_keys(%{a: 1, b: 2, c: 3}, a: :aaa, z: :zzz)
%{aaa: 1, b: 2, c: 3}

iex> Miss.Map.rename_keys(%{a: 1, b: 2, c: 3}, a: :c)
%{b: 2, c: 1}

iex> Miss.Map.rename_keys(%{a: 1, b: 2, c: 3}, c: :a)
%{a: 1, b: 2}

iex> Miss.Map.rename_keys(%{a: 1, b: 2, c: 3}, [])
%{a: 1, b: 2, c: 3}