web-dev-qa-db-fra.com

Comment convertir des clés de carte de chaînes en atomes dans Elixir

Quel est le moyen de convertir %{"foo" => "bar"} à %{foo: "bar"} dans Elixir?

62
NoDisplayName

Utilisez Compréhensions :

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

Je pense que la meilleure façon de faire est d’utiliser Map.new:

%{"a" => 1, "b" => 2} 
|> Map.new(fn {k, v} -> {String.to_atom(k), v} end)      

=> %{a: 1, b: 2}
41
PlagueHammer

Vous pouvez utiliser une combinaison de Enum.reduce/ et String.to_atom/1

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

Cependant, vous devriez vous méfier de la conversion en atomes en fonction de la saisie de l'utilisateur, car ils ne seront pas mis au rebut, ce qui peut entraîner une fuite de mémoire. Voir ce numéro .

Vous pouvez utiliser String.to_existing_atom/1 pour éviter cela si atom existe déjà.

22
Gazler

Pour vous appuyer sur la réponse de @ emaillenin, vous pouvez vérifier si les clés sont déjà des atomes, afin d'éviter le ArgumentError qui est généré par String.to atom lorsqu'il obtient une clé qui est déjà un atome.

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

Il existe une bibliothèque pour cela, https://hex.pm/packages/morphix . Il a également une fonction récursive pour les clés intégrées.

La majeure partie du travail est effectuée dans cette fonction:

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

Qui s'appelle récursivement. Après avoir lu la réponse de @ Galzer, je vais probablement convertir ceci en utilisant String.to_existing_atom bientôt.

6
philosodad

Voici une version de la réponse de @ emaillenin sous forme de module:

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
4
Jason Axelson
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(%{})
1
Chris Almeida

Voici ce que j’utilise pour récursivement (1) formater les clés de carte en tant que snakecase et (2) les convertir en atomes. N'oubliez pas que vous devez jamais convertir les données utilisateur non inscrites sur la liste blanche en atomes, car elles ne sont pas récupérées.

defp snake_case_map(map) when is_map(map) do
  Enum.reduce(map, %{}, fn {key, value}, result ->
    Map.put(result, String.to_atom(Macro.underscore(key)), snake_case_map(value))
  end)
end
defp snake_case_map(list) when is_list(list), do: Enum.map(list, &snake_case_map/1)
defp snake_case_map(value), do: value
1
Betree
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
1
Zubair Nabi