Elixir Blog Post: Avoiding Atomic Angst in Elixir

I write about the frustration that can occur when dealing with string and atom-based keys in maps and how to (safely) translate string-based map keys to atoms:


Isn’t it easier to translate all keys to string, I don’t see any downsides for that as cast/4 casts required params.

Thanks for the writeup, learnt a lot. Ability to use when in anonymous function was enlightening.

I am learning Elixir, and thought I would give list comprehension a try:

params = %{:foo => "Foo", "bar" => "Bar"}

# Convert keys to String:
for {k, v} <- params, into: %{}, do: if is_atom(k), do: {Atom.to_string(k), v}, else: {k, v}
# %{"bar" => "Bar", "foo" => "Foo"}

# Convert keys to Atoms:
for {k, v} <- params, into: %{}, do: if is_binary(k), do: {String.to_existing_atom(k), v}, else: {k, v}
# %{bar: "Bar", foo: "Foo"}

I will never mixup the two, anything external will always be string, and hand written values will be atoms.

Makes sense to me!

Assuming you just want to translate the params to be atom-based (and you’re not directly creating a record with Ecto), are you thinking something like this…?

data = %{}
types = %{title: :string}
changeset = Ecto.Changeset.cast({data, types}, %{"title" => "foo"}, [:title])

I imagine that working well if you have Ecto in the mix and don’t need to give feedback on invalid params.

I would be looking at Ecto.Changeset.apply_action/2 to return either {:ok, struct} or {:error, changeset} which contains errors in the 2nd case. But it certainly depends on ecto (though not ecto_sql).

1 Like

I wasn’t writing this with Ecto in mind, but I usually do have Ecto in the mix, so I like that.

It also feels cleaner to use the return of the apply_action function (which can include validation errors) as opposed to directly relying on the changes field in the Changeset struct (even though I think the field is more or less considered “public”).