iirc Access.filter is like Access.all, it does not really return a list, but taps into the list it is given, and sends each item to the next “accessor” so it could access it.
For example, when you did get_in(list, ["children", Access.filter(fn i -> i["amount"] > 9 end)]) then when "children" (Accessor) is encountered, then list["children"] is passed to the next one, which was Access.filter.
However, when Access.filter is encountered, it would be like a foreach where each item the predicate returns true for will be accessed by the next in command.
Now, Access.at expects a List when it is encountered, so, if you did get_in(list, ["children", Access.at(0)] you would get a result, as list["children"] is a list, however, when you hit Access.filter, it is basically a function that is throwing one by one for the next item to “access” and “collect”, and that’s a function, not a list. That’s why Access.at won’t work. However, if you put "amount" it would work, because it would do a [] on each returned element, and get it.
If you wanted 1-th item, you could get out of get_in and do an Enum.at(1) perhaps. Not sure if get_in would have a visually appealing way of doing it, but I could be wrong.
I’m not sure if it’s relevant for what you’re trying to achieve, but mentioning it just in case.
If you want the first matching element (at(0)) instead of the second one (at(1)), the newly added Access.find/1 might be what you need:
Here’s a lightly-modified version of Access.filter that does what you want:
defmodule FilterWhole do
def filter(func) when is_function(func) do
fn op, data, next -> filter(op, data, func, next) end
end
defp filter(:get, data, func, next) when is_list(data) do
data |> Enum.filter(func) |> next.()
end
defp filter(:get_and_update, data, func, next) when is_list(data) do
# need to call next.(data) and then handle tuple or :pop... somehow
raise 'what should happen here'
end
defp filter(_op, data, _func, _next) do
raise "FilterWhole.filter/1 expected a list, got: #{inspect(data)}"
end
end
data = %{
children: [%{amount: 1}, %{amount: 2}, %{amount: 3}]
}
result = get_in data, [:children, FilterWhole.filter(fn %{amount: x} -> rem(x, 2) > 0 end), Access.at(1)]
However, I’m not sure what doing a pop or an update means in this situation so that part of the function doesn’t work.