[Proposal] Extend Access.find/1 and Access.filter/1 to support maps

19 views
Skip to first unread message

Philip Munksgaard

unread,
Jul 1, 2024, 2:27:30 AM (5 days ago) Jul 1
to elixir-l...@googlegroups.com
Hi,

I'm a frequent user of the Access module, and I'm excited about the recent addition of Access.find/1 to support some use-cases I've had to work my way around in the past. However, now that I have it, the first thing I wanted to do actually applied to a map.

For context, I'm working with Ecto changesets in a Phoenix LiveView application. I'm working with a `Foo` schema that has an assoc containing a number of uploaded files. Whenever I edit a Foo, I want to be able to upload a new file, but only one at a time. I also want to be able to edit the attached descriptions to each of the files that are already uploaded. I have written some logic that only shows one live_file_input at a time, and I have written the following `save` function (following the pattern in the standard phx.gen.live templates):

```elixir
defp save(socket, :edit, foo_params) do
uploaded_entries_params = consume_uploaded_entries(socket, :file, &upload_consumer/2)

foo_params =
case uploaded_entries_params do
[] ->
foo_params

[upload_params] ->
update_in(
foo_params,
["foo_files", Access.find(&is_nil(&1["file_id"])), "file"],
&Map.merge(&1, upload_params)
)
end

case Foos.update_foo(socket.assigns.foo, foo_params) do
{:ok, foo} ->
{:noreply, push_patch(socket, to: socket.assigns.patch)}

{:error, %Ecto.Changeset{} = changeset} ->
{:noreply, assign_form(socket, changeset)}
end
end
```

The interesting bit is the `update_in` call, which takes the params generated by my `upload_consumer` and merges it with the params coming from the form submission. Unfortunately, this doesn't work, since `foo_params["foo_files"]` is a map of the form: `%{"0" => %{ ... }, ...}`

I was a bit surprised to find that Access.find/1 doesn't work with maps, but then I remembered that the same is true for Access.filter/1. My (very) cursory look at the implementation suggests that both of those are using Enum-functions, which _do_ in fact support maps, so I am wondering why that is not the case for the Access-functions? Would there be any interest in adding support for maps in Access.find/1 and Access.filter/1?

Best regards
Philip

José Valim

unread,
Jul 1, 2024, 2:41:24 AM (5 days ago) Jul 1
to elixir-l...@googlegroups.com
The general problem is, when used with the update operations, both find/1 and filter/1 would have to build the original shape back, instead of simply returning a list (which is what the Enum functions do). It could be done for maps and filter, but I am not quite sure about find. Also, we need to be careful to not promote traversals in maps (by using find) instead of doing a per key operation.

--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/d29a0d69-db72-4d2d-814e-5809c4f8da6f%40app.fastmail.com.

Aleksei Matiushkin

unread,
Jul 1, 2024, 3:06:20 AM (5 days ago) Jul 1
to elixir-lang-core
> It could be done for maps and filter, but I am not quite sure about find.

Well, technically `find` is `filter` in a nutshell, with some additional condition, so it’s either or none.

> we need to be careful to not promote traversals in maps (by using find) instead of doing a per key operation.

I’d say they are by no means interchangeable because nobody (hopefully) filters/finds by key, these functions are normally used as a last resort to lookup by value, and it’s next to impossible in many cases to reshape the input so that filtering might be done by key to avoid traversal. 

José Valim

unread,
Jul 1, 2024, 3:16:14 AM (5 days ago) Jul 1
to elixir-l...@googlegroups.com
The concern is exactly with the "(hopefully)". :)

Reply all
Reply to author
Forward
0 new messages