0.9.3 - this version is safe to use because it has no known security vulnerabilities at this time. Find out if your coding project uses this component and get notified of any reported security vulnerabilities with Meterian-X Open Source Security Platform
Maintain your licence declarations and avoid unwanted licences to protect your IP the way you intended.
MIT - MIT LicenseA small utility to convert Elixir maps with mixed string/atom keys to atom-only keyed maps. Optionally with a safe option, to prevent atom space exhaustion of the Erlang VM. Since v0.8 it also supports conversion of keys from CamelCase
to under_score
format.
AtomicMap can convert simple maps or nested structures such as lists of maps; see Nested Structures below for examples.
With no options, it safely converts string keys in maps to atoms, using String.to_existing_atom/1
.
iex> AtomicMap.convert(%{"a" => "a", "b" => "b", c: "c"})
%{a: "a", b: "b", c: "c"}
Because safe conversion uses String.to_existing_atom/1
, it will raise when the target atom does not exist.
iex> AtomicMap.convert(%{"abcdefg" => "a", "b" => "b"})
** (ArgumentError) argument error
:erlang.binary_to_existing_atom("abcdefg", :utf8)
To have safe conversion can ignore unsafe keys, leaving them as strings, pass true
for the ignore
option.
iex> AtomicMap.convert(%{"abcdefg" => "a", "b" => "b"}, %{ignore: true})
%{:b => "b", "abcdefg" => "a"}
To disable safe conversion and allow new atoms to be created, pass false
for the safe:
option.
(This makes the ignore
option irrelevant.)
If the input is user-generated, converting only expected keys will prevent excessive atom creation.
iex> map = %{"expected_key" => "a", "b" => "b", "unexpected_key" => "c"}
%{"expected_key" => "a", "b" => "b", "unexpected_key" => "c"}
iex> filtered_map = Map.take(map, ["expected_key", "b"])
%{"b" => "b", "expected_key" => "a"}
iex> AtomicMap.convert(filtered_map, %{safe: false})
%{b: "b", expected_key: "a"}
By default, "CamelCase"
string keys will be converted to under_score
atom keys.
iex> AtomicMap.convert(%{ "CamelCase" => "hi" })
** (ArgumentError) argument error
:erlang.binary_to_existing_atom("camel_case", :utf8)
Note that "camel_case"
was the string that failed conversion.
If that atom is explicitly created first, the conversion will succeed.
iex> :camel_case
:camel_case
iex> AtomicMap.convert(%{ "CamelCase" => "hi" })
%{camel_case: "hi"}
Allowing unsafe conversions will also work. If the input is user-generated, converting only expected keys will prevent excessive atom creation.
iex> map = %{"CamelCase" => "a", "b" => "b", "AnotherCamelCase" => "c"}
%{"CamelCase" => "a", "b" => "b", "AnotherCamelCase" => "c"}
iex> filtered_map = Map.take(map, ["CamelCase", "b"])
%{"b" => "b", "CamelCase" => "a"}
iex> AtomicMap.convert(filtered_map, %{safe: false})
%{b: "b", camel_case: "a"}
"hyphenated-string"
keys will always be converted to under_score
atom keys.
There is currently no way to disable this behavior.
iex> AtomicMap.convert(%{"some-key" => "a", "b" => "c"})
** (ArgumentError) argument error
:erlang.binary_to_existing_atom("some_key", :utf8)
Note that "some_key"
was the string that failed conversion.
If that atom is explicitly created first, the conversion will succeed.
iex> :some_key
:some_key
iex> AtomicMap.convert(%{"some-key" => "a", "b" => "c"})
%{b: "c", some_key: "a"}
Allowing unsafe conversions will also work. If the input is user-generated, converting only expected keys will prevent excessive atom creation.
iex> map = %{"some-key" => "a", "b" => "b", "another-key" => "c"}
%{"some-key" => "a", "b" => "b", "another-key" => "c"}
iex> filtered_map = Map.take(map, ["some-key", "b"])
%{"b" => "b", "some-key" => "a"}
iex> AtomicMap.convert(filtered_map, %{safe: false})
%{b: "b", some_key: "a"}
# works with nested maps
iex> AtomicMap.convert(%{"a" => 2, "b" => %{"c" => 4}})
%{a: 2, b: %{c: 4}}
# works with nested maps + lists + mixed key types (atoms + binaries)
iex> AtomicMap.convert([ %{"c" => 1}, %{:c => 2}, %{"c" => %{:b => 4}}], %{safe: true})
[%{c: 1}, %{c: 2}, %{c: %{b: 4}}]
Add atomic_map to your list of dependencies in mix.exs
:
def deps do [{:atomic_map, "~> 0.8"}] end
$ mix bench