Skip to content

Instantly share code, notes, and snippets.

@am-kantox
Created November 1, 2019 11:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save am-kantox/0633542e09d6faef26a7c5a1f28f84c9 to your computer and use it in GitHub Desktop.
Save am-kantox/0633542e09d6faef26a7c5a1f28f84c9 to your computer and use it in GitHub Desktop.
`MapAccess.filter/1` → the same as `Access.filter/1`, but works with maps.
defmodule MapAccess do
@spec filter((term -> boolean)) :: Access.access_fun(data :: map, get_value :: map)
def filter(func) when is_function(func, 1) do
fn op, data, next -> filter(op, data, func, next) end
end
defp filter(:get, %{} = data, func, next) do
data |> Enum.filter(func) |> Enum.map(next) |> Map.new()
end
defp filter(:get_and_update, %{} = data, func, next) do
get_and_update_filter(Map.to_list(data), func, next, [], [])
end
defp filter(_op, data, _func, _next) do
raise "MapAccess.filter/1 expected a map, got: #{inspect(data)}"
end
defp get_and_update_filter([{k, v} = head | rest], func, next, updates, gets) do
if func.(head) do
case next.(v) do
{get, update} ->
get_and_update_filter(rest, func, next, [{k, update} | updates], [head | gets])
:pop ->
get_and_update_filter(rest, func, next, updates, [head | gets])
end
else
get_and_update_filter(rest, func, next, [head | updates], gets)
end
end
defp get_and_update_filter([], _func, _next, updates, gets) do
{Map.new(gets), Map.new(updates)}
end
end
input = %{
payload: %{
"foo" => 42,
"bar" => 5,
"baz" => 10
}
}
filter_fun = MapAccess.filter(&match?({"b" <> _, _}, &1))
update_fun = &(&1 * 2)
get_and_update_in_fun = &{&1, &1 * 2}
get_in(input, [:payload, filter_fun])
# ⇒ %{"bar" => 5, "baz" => 10}
put_in(input, [:payload, filter_fun], 42)
# ⇒ %{payload: %{"bar" => 42, "baz" => 42, "foo" => 42}}
update_in(input, [:payload, filter_fun], update_fun)
# ⇒ %{payload: %{"bar" => 10, "baz" => 20, "foo" => 42}}
get_and_update_in(input, [:payload, filter_fun], get_and_update_in_fun)
# ⇒ {%{"bar" => 5, "baz" => 10},
# %{payload: %{"bar" => 10, "baz" => 20, "foo" => 42}}}
pop_in(input, [:payload, filter_fun])
# ⇒ {%{"bar" => 5, "baz" => 10}, %{payload: %{"foo" => 42}}}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment