Alex Antonov
Alex Antonov

Reputation: 15156

How to get value by key from map, no matter - key is string or atom?

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

Answers (2)

Sheharyar
Sheharyar

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

Aleksei Matiushkin
Aleksei Matiushkin

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

Related Questions