Как преобразовать ключи карты из строк в атомы в Elixir

Как преобразовать %{"foo" => "bar"} в %{foo: "bar"} в Elixir?

Ответ 1

Используйте Comprehensions:

iex(1)> string_key_map = %{"foo" => "bar", "hello" => "world"}
%{"foo" => "bar", "hello" => "world"}

iex(2)> for {key, val} <- string_key_map, into: %{}, do: {String.to_atom(key), val}
%{foo: "bar", hello: "world"}

Ответ 2

Вы можете использовать комбинацию Enum.reduce/3 и String.to_atom/1

%{"foo" => "bar"}
|> Enum.reduce(%{}, fn ({key, val}, acc) -> Map.put(acc, String.to_atom(key), val) end)

Однако вы должны быть осторожны при конвертации в атомы, основанные на пользовательском вводе, поскольку они не будут собирать мусор, что может привести к утечке памяти. См. эту проблему.

Вы можете использовать String.to_existing_atom/1, чтобы предотвратить это, если атом уже существует.

Ответ 3

Я думаю, что самый простой способ сделать это - использовать Map.new:

h = %{"foo" => "bar"}

Map.new(h, fn {k, v} -> {String.to_atom(k), v} end)

=> %{foo: "bar"}

Ответ 4

Чтобы построить ответ на @emaillenin, вы можете проверить, не являются ли ключи уже атомами, чтобы избежать ArgumentError, который был вызван String.to_atom, когда он получает ключ, который уже является атомом.

for {key, val} <- string_key_map, into: %{} do
  cond do
    is_atom(key) -> {key, val}
    true -> {String.to_atom(key), val}
  end
end

Ответ 5

Там есть библиотека для этого https://hex.pm/packages/morphix. Он также имеет рекурсивную функцию для встроенных ключей.

Большая часть работы выполняется в этой функции:

defp atomog (map) do
    atomkeys = fn({k, v}, acc) ->
      Map.put_new(acc, atomize_binary(k), v)
    end
    Enum.reduce(map, %{}, atomkeys)
  end

  defp atomize_binary(value) do 
    if is_binary(value), do: String.to_atom(value), else: value
  end

Это называется рекурсивно. После прочтения ответа @Galzer я, скорее всего, конвертирую это, чтобы скоро использовать String.to_existing_atom.

Ответ 6

Здесь версия ответа @emaillenin в форме модуля:

defmodule App.Utils do

  # Implementation based on: http://stackoverflow.com/a/31990445/175830
  def map_keys_to_atoms(map) do
    for {key, val} <- map, into: %{}, do: {String.to_atom(key), val}
  end

  def map_keys_to_strings(map) do
    for {key, val} <- map, into: %{}, do: {Atom.to_string(key), val}
  end

end

Ответ 7

defmodule Service.MiscScripts do

@doc """
Changes String Map to Map of Atoms e.g. %{"c"=> "d", "x" => %{"yy" => "zz"}} to
        %{c: "d", x: %{yy: "zz"}}, i.e changes even the nested maps.
"""

def  convert_to_atom_map(map), do: to_atom_map(map)

defp to_atom_map(map) when is_map(map), do: Map.new(map, fn {k,v} -> {String.to_atom(k),to_atom_map(v)} end)     
defp to_atom_map(v), do: v

end

Ответ 8

m = %{"key" => "value", "another_key" => "another_value"}
k = Map.keys(m)|> Enum.map(&(String.to_atom(&1)))
v = Map.values(m)
result = Enum.zip(k, v) |> Enum.into(%{})