Warnung: [ String.to_atom/1] erzeugt Atome dynamisch und Atome werden nicht durch Müll gesammelt. Daher sollte die Zeichenfolge kein nicht vertrauenswürdiger Wert sein, z. B. eine Eingabe, die von einem Socket oder während einer Webanforderung empfangen wird. Verwenden Sie stattdessen to_existing_atom / 1. hexdocs.pm/elixir/String.html#to_atom/1
wie man unten in Atomketten umwandelt [% {"foo" => "bar", "hallo" => "world"},% {"foo" => "baromater", "hallo" => "nope"} ]
Rizwan Patel
2
Hier ist eine Funktionsdefinition für einen rekursiven Helfer: def keys_to_atoms(string_key_map) when is_map(string_key_map) do for {key, val} <- string_key_map, into: %{}, do: {String.to_atom(key), keys_to_atoms(val)} enddef keys_to_atoms(value), do: value
Olshansk
9
Warnung! Sie sollten dies nicht bei nicht vertrauenswürdigen Benutzereingaben aufrufen, da bei Atomen kein Müll gesammelt wird und Sie möglicherweise an
Jason Axelson
1
Beachten Sie nur, dass dies nicht tief / rekursiv konvertiert
sn3p
52
Ich denke, der einfachste Weg, dies zu tun, ist Map.new:
Sie sollten jedoch vorsichtig sein, wenn Sie aufgrund von Benutzereingaben in Atome konvertieren, da diese nicht durch Müll gesammelt werden, was zu einem Speicherverlust führen kann. Siehe dieses Problem .
Ja, ich weiß über Speicherverlust, dasselbe war vor 2.2 in Ruby, aber trotzdem danke
NoDisplayName
10
Ja, es war dasselbe in Ruby, aber Elixir ist nicht Ruby und im Allgemeinen wird dieses Muster in Elixir extrem entmutigt. Der einzige Fall, den ich für sinnvoll halten kann, ist das Laden von Daten in Strukturen (und dann gibt es sicherere Möglichkeiten)
José Valim
11
@ JoséValim Was ist der sicherere Weg, um Daten in Strukturen zu laden?
Letseatlunch
7
Um auf der Antwort von @ emaillenin aufzubauen, können Sie überprüfen, ob die Schlüssel bereits Atome sind, um zu vermeiden, ArgumentErrordass String.to_atom einen Schlüssel erhält, der bereits ein Atom ist.
for {key, val} <- string_key_map, into: %{} do
cond do
is_atom(key) -> {key, val}
true -> {String.to_atom(key), val}
end
end
Hierfür gibt es eine Bibliothek, https://hex.pm/packages/morphix . Es hat auch eine rekursive Funktion für eingebettete Schlüssel.
Die meiste Arbeit wird in dieser Funktion erledigt:
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
Welches heißt rekursiv. Nachdem ich die Antwort von @ Galzer gelesen habe, werde ich sie wahrscheinlich String.to_existing_atombald konvertieren .
Wie ist das rekursiv? atomog(%{"a" => %{"b" => 2}})kehrt zurück%{a: %{"b" => 2}}
sn3p
Die Atomog-Funktion wird als Teil einer rekursiven Funktion aufgerufen, sie ist selbst nicht rekursiv. Überprüfen Sie den Morphix-Code selbst auf weitere Details.
Philosodad
4
Hier ist eine Version der Antwort von @ emaillenin in Modulform:
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
Zunächst einmal hat die Antwort von @ Olshansk für mich wie ein Zauber gewirkt. Danke für das.
Da die anfängliche Implementierung von @Olshansk die Liste der Karten nicht unterstützte , ist unten mein Code-Snippet aufgeführt, das dies erweitert.
def keys_to_atoms(string_key_map) when is_map(string_key_map) do
for {key, val} <- string_key_map, into: %{}, do: {String.to_atom(key), keys_to_atoms(val)}
end
def keys_to_atoms(string_key_list) when is_list(string_key_list) do
string_key_list
|> Enum.map(&keys_to_atoms/1)
end
def keys_to_atoms(value), do: value
Dies ist das Beispiel, das ich verwendet habe, gefolgt von der Ausgabe, nachdem es an die obige Funktion übergeben wurde - keys_to_atoms(attrs)
Die Erklärung dafür ist sehr einfach. Die erste Methode ist das Herzstück von allem, was für die Eingabe der Typzuordnung aufgerufen wird. Die for-Schleife zerstört die Attribute in Schlüssel-Wert-Paaren und gibt die Atomdarstellung des Schlüssels zurück. Als nächstes gibt es bei der Rückgabe des Werts wieder drei Möglichkeiten.
Der Wert ist eine weitere Karte.
Der Wert ist eine Liste von Karten.
Der Wert ist keiner der oben genannten, er ist primitiv.
Wenn die keys_to_atomsMethode dieses Mal aufgerufen wird, während ein Wert zugewiesen wird, kann sie je nach Art der Eingabe eine der drei Methoden aufrufen. Die Methoden sind im Snippet in ähnlicher Reihenfolge angeordnet.
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
Hier ist, was ich verwende, um rekursiv (1) Kartenschlüssel als Schlangenfall zu formatieren und (2) sie in Atome umzuwandeln. Beachten Sie, dass Sie niemals nicht auf der Whitelist stehende Benutzerdaten in Atome konvertieren sollten , da diese nicht durch Müll gesammelt werden.
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
def keys_to_atom(map) do
Map.new(
map,
fn {k, v} ->
v2 = cond do
is_map(v) -> keys_to_atom(v)
v in [[nil], nil] -> nil
is_list(v) -> Enum.map(v, fn o -> keys_to_atom(o) end)
true -> v
end
{String.to_atom("#{k}"), v2}
end
)
end
String.to_atom/1
] erzeugt Atome dynamisch und Atome werden nicht durch Müll gesammelt. Daher sollte die Zeichenfolge kein nicht vertrauenswürdiger Wert sein, z. B. eine Eingabe, die von einem Socket oder während einer Webanforderung empfangen wird. Verwenden Sie stattdessen to_existing_atom / 1. hexdocs.pm/elixir/String.html#to_atom/1Antworten:
Verwendung Comprehensions :
quelle
def keys_to_atoms(string_key_map) when is_map(string_key_map) do for {key, val} <- string_key_map, into: %{}, do: {String.to_atom(key), keys_to_atoms(val)} end
def keys_to_atoms(value), do: value
Ich denke, der einfachste Weg, dies zu tun, ist
Map.new
:quelle
Sie können eine Kombination aus Enum.reduce / 3 und String.to_atom / 1 verwenden
Sie sollten jedoch vorsichtig sein, wenn Sie aufgrund von Benutzereingaben in Atome konvertieren, da diese nicht durch Müll gesammelt werden, was zu einem Speicherverlust führen kann. Siehe dieses Problem .
Sie können String.to_existing_atom / 1 verwenden , um dies zu verhindern, wenn das Atom bereits vorhanden ist.
quelle
Um auf der Antwort von @ emaillenin aufzubauen, können Sie überprüfen, ob die Schlüssel bereits Atome sind, um zu vermeiden,
ArgumentError
dass String.to_atom einen Schlüssel erhält, der bereits ein Atom ist.quelle
Hierfür gibt es eine Bibliothek, https://hex.pm/packages/morphix . Es hat auch eine rekursive Funktion für eingebettete Schlüssel.
Die meiste Arbeit wird in dieser Funktion erledigt:
Welches heißt rekursiv. Nachdem ich die Antwort von @ Galzer gelesen habe, werde ich sie wahrscheinlich
String.to_existing_atom
bald konvertieren .quelle
atomog(%{"a" => %{"b" => 2}})
kehrt zurück%{a: %{"b" => 2}}
Hier ist eine Version der Antwort von @ emaillenin in Modulform:
quelle
Zunächst einmal hat die Antwort von @ Olshansk für mich wie ein Zauber gewirkt. Danke für das.
Da die anfängliche Implementierung von @Olshansk die Liste der Karten nicht unterstützte , ist unten mein Code-Snippet aufgeführt, das dies erweitert.
Dies ist das Beispiel, das ich verwendet habe, gefolgt von der Ausgabe, nachdem es an die obige Funktion übergeben wurde -
keys_to_atoms(attrs)
Die Erklärung dafür ist sehr einfach. Die erste Methode ist das Herzstück von allem, was für die Eingabe der Typzuordnung aufgerufen wird. Die for-Schleife zerstört die Attribute in Schlüssel-Wert-Paaren und gibt die Atomdarstellung des Schlüssels zurück. Als nächstes gibt es bei der Rückgabe des Werts wieder drei Möglichkeiten.
Wenn die
keys_to_atoms
Methode dieses Mal aufgerufen wird, während ein Wert zugewiesen wird, kann sie je nach Art der Eingabe eine der drei Methoden aufrufen. Die Methoden sind im Snippet in ähnlicher Reihenfolge angeordnet.Hoffe das hilft. Prost!
quelle
quelle
quelle
Hier ist, was ich verwende, um rekursiv (1) Kartenschlüssel als Schlangenfall zu formatieren und (2) sie in Atome umzuwandeln. Beachten Sie, dass Sie niemals nicht auf der Whitelist stehende Benutzerdaten in Atome konvertieren sollten , da diese nicht durch Müll gesammelt werden.
quelle
Das folgende Snippet konvertiert Schlüssel einer verschachtelten json-ähnlichen Karte in vorhandene Atome:
iex(2)> keys_to_atoms(%{"a" => %{"b" => [%{"c" => "d"}]}}) %{a: %{b: [%{c: "d"}]}}
def keys_to_atoms(json) when is_map(json) do Map.new(json, &reduce_keys_to_atoms/1) end def reduce_keys_to_atoms({key, val}) when is_map(val), do: {String.to_existing_atom(key), keys_to_atoms(val)} def reduce_keys_to_atoms({key, val}) when is_list(val), do: {String.to_existing_atom(key), Enum.map(val, &keys_to_atoms(&1))} def reduce_keys_to_atoms({key, val}), do: {String.to_existing_atom(key), val}
quelle
wenn Sie eine Karte in einer anderen Karte haben
Stichprobe:
Ergebnis
%{a: "1", b: [%{b1: "1"}], c: %{d: "4"}}
Hinweis: Die is_list schlägt fehl, wenn Sie "b" => [1,2,3] haben, sodass Sie diese Zeile kommentieren / entfernen können, wenn dies der Fall ist:
quelle