Reputation: 15156
Is there any native elixir method which allows me to get value by key from map, no matter key is atom or string? Like:
a = %{ k: 1 }
b = %{ "k" => 1 }
pure_elixir_method(a, :k) => 1
pure_elixir_method(b, :k) => 1
Upvotes: 3
Views: 3124
Reputation: 75760
There isn't one.
But you can write your own wrapper method to do that:
defmodule MyMap do
def get(map, key) when is_atom(key) do
Map.get(map, key) || Map.get(map, to_string(key))
end
end
Use it like this:
map = %{:a => 1, "b" => 2}
MyMap.get(map, :a) # => 1
MyMap.get(map, :b) # => 2
1. Just because you can, doesn't mean you should. %{k: 1, "k" => 2}
is an excellent example why you shouldn't do this (especially for data you don't know about).
2. This method only works for atom
key arguments, you can modify it so it accepts both atom and string arguments: MyMap.get(map, "a") # => 1
3. If you really like getting map values using atoms, consider symbolizing keys of your map at the start. See my ExUtils.Map.symbolize_keys/2
. If you want to implement it yourself, you can get the code here.
Upvotes: 5
Reputation: 121000
Well, the desired behaviour might be achieved with defining the specific protocol:
defprotocol IndifferentAccess do
def get(data, key)
end
defimpl IndifferentAccess, for: Map do
def get(data, key) when is_binary(key) do
case Map.fetch(data, key) do
{:ok, value} -> value
:error ->
case Map.fetch(data, String.to_atom(key)) do
{:ok, value} -> value
:error -> :error
end
end
end
end
def get(data, key) when is_atom(key) do
case Map.fetch(data, key) do
{:ok, value} -> value
:error -> get(data, Atom.to_string(key))
end
end
end
a = %{ k: 1 }
b = %{ "k" => 1 }
a |> IndifferentAccess.get(:k)
#⇒ 1
b |> IndifferentAccess.get(:k)
#⇒ 1
But I won’t do that.
Upvotes: 3