defmodule AtomizeKeys do
@moduledoc """
Documentation for AtomizeKeys.
"""
@doc """
Change string map keys to atoms.
## Examples
iex> AtomizeKeys.atomize_string_keys(%{"a" => 1, "b" => 2})
%{a: 1, b: 2}
"""
def atomize_string_keys(map) when is_map(map), do: atomize_keys(map)
def atomize_string_keys(term) when is_list(term), do: Enum.map(term, &atomize_keys/1)
def atomize_string_keys(not_a_map), do: not_a_map
@doc """
Change atom map keys to strings.
## Examples
iex> AtomizeKeys.stringify_atom_keys(%{a: 1, b: 2})
%{"a" => 1, "b" => 2}
"""
def stringify_atom_keys(%_{} = term), do: term
def stringify_atom_keys(%{} = term), do: stringify_keys(term)
def stringify_atom_keys(term) when is_list(term), do: Enum.map(term, &stringify_keys/1)
def stringify_atom_keys(term), do: term
defp stringify_keys(nil), do: nil
defp stringify_keys(%_{} = struct), do: struct
defp stringify_keys(%{} = map) do
map
|> Enum.map(fn
{k, v} when is_atom(k) -> {to_string(k), stringify_keys(v)}
{k, v} -> {k, stringify_keys(v)}
end)
|> Enum.into(%{})
end
defp stringify_keys([head | rest]), do: [stringify_keys(head) | stringify_keys(rest)]
defp stringify_keys(not_a_map), do: not_a_map
defp atomize_keys(nil), do: nil
# Structs don't do enumerable and the keys are already atoms
defp atomize_keys(%_{} = struct), do: struct
defp atomize_keys(%{} = map) do
map
|> Enum.map(fn
{k, v} when is_binary(k) -> {to_atom(k), atomize_keys(v)}
{k, v} -> {k, atomize_keys(v)}
end)
|> Enum.into(%{})
end
defp atomize_keys([head | rest]), do: [atomize_keys(head) | atomize_keys(rest)]
defp atomize_keys(not_a_map), do: not_a_map
defp to_atom(string) do
try do
String.to_existing_atom(string)
rescue
ArgumentError -> String.to_atom(string)
end
end
end