silviurosu
Convert multiple map values conditionally
I need to convert in place in a map some values only of they exist. The key can be either binary or atom.
Is there a more Elixir way to do it than my solution below?
defp convert_time_params(params) do
params
|> (fn x ->
if Map.has_key?(x, "start_time"), do: update_in(x, ["start_time"], &CalendarUtils.from_iso8601/1), else: x
end).()
|> (fn x ->
if Map.has_key?(x, :start_time),
do: update_in(x, [Access.key!(:start_time)], &CalendarUtils.from_iso8601/1),
else: x
end).()
|> (fn x ->
if Map.has_key?(x, "end_time"), do: update_in(x, ["end_time"], &CalendarUtils.from_iso8601/1), else: x
end).()
|> (fn x ->
if Map.has_key?(x, :end_time),
do: update_in(x, [Access.key!(:end_time)], &CalendarUtils.from_iso8601/1),
else: x
end).()
end
The map is used in Ecto so I can not change the key type from atom to binary. I need to keep the input the same
Most Liked
hauleth
def update_existing(map, key, fun) do
case map do
%{^key => old} -> %{map | key => fun.(old)}
_ -> map
end
end
def update_indifferent(map, key, fun) when is_atom(key) do
map
|> update_existing(key, fun)
|> update_existing(Atom.to_string(key), fun)
end
And then
params
|> update_indifferent(:start_time, &CalendarUtils.from_iso8601/1)
|> update_indifferent(:end_time, &CalendarUtils.from_iso8601/1)
kip
I have a function called deep_map that I use in several different projects to simplify certain classes of map operations. On top of that I have other functions like atomize_keys/3 and so on. It might be useful (or it might be not
)
Summary
Recursively traverse a map and invoke a function
that transforms the map for each key/value pair.
Arguments
mapis anyt:map/0functionis a1-arityfunction or function reference that
is called for each key/value pair of the provided map. It can
also be a 2-tuple of the form{key_function, value_function}- In the case where
functionis a single function it will be
called with the 2-tuple argument{key, value} - In the case where function is of the form
{key_function, value_function}
thekey_functionwill be called with the argumentkeyand the value
function will be called with the argumentvalue
- In the case where
optionsis a keyword list of options. The default is[]
Options
:levelindicates the starting (and optionally ending) levels of
the map at which thefunctionis executed. This can
be an integer representing onelevelor a range
indicating a range of levels. The default is1..#{@max_level}:onlyis a term or list of terms or acheck function. If it is a term
or list of terms, thefunctionis only called if thekeyof the
map is equal to the term or in the list of terms. If:onlyis a
check functionthen thecheck functionis passed the{k, v}of
the current branch in themap. It is expected to return atruthy
value that iftruesignals that the argumentfunctionwill be executed.:exceptis a term or list of terms or acheck function. If it is a term
or list of terms, thefunctionis only called if thekeyof the
map is not equal to the term or not in the list of terms. If:exceptis a
check functionthen thecheck functionis passed the{k, v}of
the current branch in themap. It is expected to return atruthy
value that iftruesignals that the argumentfunctionwill not be executed.
Notes
If both the options :only and :except are provided then the function
is called only when a term meets both criteria.
Returns
- The
maptransformed by the recursive application of
function
Examples
iex> map = %{a: :a, b: %{c: :c}}
iex> fun = fn
...> {k, v} when is_atom(k) -> {Atom.to_string(k), v}
...> other -> other
...> end
iex> Cldr.Map.deep_map map, fun
%{"a" => :a, "b" => %{"c" => :c}}
iex> map = %{a: :a, b: %{c: :c}}
iex> Cldr.Map.deep_map map, fun, only: :c
%{a: :a, b: %{"c" => :c}}
iex> Cldr.Map.deep_map map, fun, except: [:a, :b]
%{a: :a, b: %{"c" => :c}}
iex> Cldr.Map.deep_map map, fun, level: 2
%{a: :a, b: %{"c" => :c}}
hauleth
It is very important to point out that this should be used ONLY if you trust source of the binaries. If used on untrusted source then you can experience DoS when malicious party will send data with a lot different strings.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









