atomic_map

A small utility to convert deep Elixir maps with mixed string/atom keys to atom-only keyed maps.

Latest version: 0.9.3 registry icon
Maintenance score
0
Safety score
0
Popularity score
71
Check your open source dependency risks. Get immediate insight about security, stability and licensing risks.
Security
  Vulnerabilities
Version Suggest Low Medium High
0.9.3 0 0 0 0
0.9.2 0 0 0 0
0.9.1 0 0 0 0
0.9.0 0 0 0 0
0.8.0 0 0 0 0
0.7.0 0 0 0 0
0.6.0 0 0 0 0
0.5.0 0 0 0 0

Stability
Latest release:

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

Licensing

Maintain your licence declarations and avoid unwanted licences to protect your IP the way you intended.

MIT   -   MIT License

Not a wildcard

Not proprietary

OSI Compliant



AtomicMap

Build status Hex version Hex downloads

A 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.

Usage

Safety

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"}

Underscoring

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"}

Replacing Hyphens

"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"}

Nested Structures

# 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}}]

Installation

  1. Add atomic_map to your list of dependencies in mix.exs:

    def deps do [{:atomic_map, "~> 0.8"}] end

Todo:

  • maybe allow direct conversion to a struct, like Poison does it: as: %SomeStruct{}...

Benchmark

$ mix bench