How to convert map keys from strings to atoms in Elixir

前端 未结 13 1183
梦毁少年i
梦毁少年i 2021-01-31 07:07

What is the way to convert %{\"foo\" => \"bar\"} to %{foo: \"bar\"} in Elixir?

相关标签:
13条回答
  • 2021-01-31 07:18

    There's a library for this, https://hex.pm/packages/morphix. It also has a recursive function for embedded keys.

    Most of the work is done in this function:

    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
    

    Which is called recursively. After reading @Galzer's answer I'll probably convert this to use String.to_existing_atom soon.

    0 讨论(0)
  • 2021-01-31 07:20

    To build on @emaillenin's answer, you can check to see if the keys are already atoms, to avoid the ArgumentError that is raised by String.to_atom when it gets a key that is already an atom.

    for {key, val} <- string_key_map, into: %{} do
      cond do
        is_atom(key) -> {key, val}
        true -> {String.to_atom(key), val}
      end
    end
    
    0 讨论(0)
  • 2021-01-31 07:23

    You can use a combination of Enum.reduce/3 and String.to_atom/1

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

    However you should be wary of converting to atoms based in user input as they will not be garbage collected which can lead to a memory leak. See this issue.

    You can use String.to_existing_atom/1 to prevent this if the atom already exists.

    0 讨论(0)
  • 2021-01-31 07:26

    Snippet below converts keys of nested json-like map to existing atoms:

    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}
    
    0 讨论(0)
  • 2021-01-31 07:27
    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
    
    0 讨论(0)
  • 2021-01-31 07:27
    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(%{})
    
    0 讨论(0)
提交回复
热议问题