Was ist der Weg zu konvertieren , %{"foo" => "bar"}
um %{foo: "bar"}
in Elixir?
Was ist der Weg zu konvertieren , %{"foo" => "bar"}
um %{foo: "bar"}
in Elixir?
Antworten:
Verwendung 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"}
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
:
%{"a" => 1, "b" => 2}
|> Map.new(fn {k, v} -> {String.to_atom(k), v} end)
=> %{a: 1, b: 2}
Sie können eine Kombination aus Enum.reduce / 3 und String.to_atom / 1 verwenden
%{"foo" => "bar"}
|> Enum.reduce(%{}, fn ({key, val}, acc) -> Map.put(acc, String.to_atom(key), val) end)
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.
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.
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_atom
bald konvertieren .
atomog(%{"a" => %{"b" => 2}})
kehrt zurück%{a: %{"b" => 2}}
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)
# Input
%{
"school" => "School of Athens",
"students" => [
%{
"name" => "Plato",
"subjects" => [%{"name" => "Politics"}, %{"name" => "Virtues"}]
},
%{
"name" => "Aristotle",
"subjects" => [%{"name" => "Virtues"}, %{"name" => "Metaphysics"}]
}
]
}
# Output
%{
school: "School of Athens",
students: [
%{name: "Plato", subjects: [%{name: "Politics"}, %{name: "Virtues"}]},
%{name: "Aristotle", subjects: [%{name: "Virtues"}, %{name: "Metaphysics"}]}
]
}
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!
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
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(%{})
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
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}
wenn Sie eine Karte in einer anderen Karte haben
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
Stichprobe:
my_map = %{"a" => "1", "b" => [%{"b1" => "1"}], "c" => %{"d" => "4"}}
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:
# is_list(v) -> Enum.map(v, fn o -> keys_to_atom(o) 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/1