sodapopcan

sodapopcan

Help me rewrite these nested reductions?

I have a form with arbitrary levels of deeply nested associations. One particular association that lives a couple of levels down can have an upload associated with it. To track these I need to allow_upload based on the indices of each level. Currently this looks like this:

socket.assigns.form.source
|> Ecto.Changeset.get_assoc(:mockups)
|> Enum.with_index()
|> Enum.reduce(socket, fn {mockup, mockup_index}, socket ->
  mockup
  |> Ecto.Changeset.get_assoc(:elements)
  |> Enum.with_index()
  |> Enum.reduce(socket, fn {element, element_index}, socket ->
    element
    |> Ecto.Changeset.get_assoc(:transformations)
    |> Enum.with_index()
    |> Enum.reduce(socket, fn {_transformation, transformation_index}, socket ->
      allow_image_upload(
        socket,
        "transformation-#{mockup_index}-#{element_index}-#{transformation_index}"
      )
    end)
  end)
end)

I’m wondering how others would go about this.

Of course I realize I can abstract out the commonalities into a private function, but I would rather not do that as this is the only place this type of thing is happening and feel it’ll make it less readable for passers-by. I’m wondering if there is a reducer pattern that can do this type of nested thing in a flat pipeline. I have a feeling Pathex can probably come in handy here but interested in other options. I’m thinking of reducing into a 2-tuple of {socket, %{}} and building up the indices in the map then doing a final reduction to build up all the keys and allow_upload them, but that feels like it will be more complex to read.

Anyway, just wondering! As verbose as my example is, I don’t hate it as it’s pretty easy to read. Credo is complaining about the indentation depth, though, and I don’t necessarily disagree with it.

Marked As Solved

100phlecs

100phlecs

It’s a great tool, one of the main reasons I like Elixir.

Yes, if you need to update the socket, then you can tack on reduce:

assoc_with_index = fn changeset, assoc ->
  changeset
  |> Ecto.Changeset.get_assoc(assoc)
  |> Enum.with_index()
end

source = socket.assigns.form.source

for {mockup, mockup_index} <- assoc_with_index.(source, :mockups),
    {element, element_index} <- assoc_with_index.(mockup, :elements),
    {_transformation, transformation_index} <- assoc_with_index.(element, :transformations),
    reduce: socket do
  socket ->
    allow_image_upload(
      socket,
      "transformation-#{mockup_index}-#{element_index}-#{transformation_index}"
    )
end

Also Liked

100phlecs

100phlecs

When you start dealing with nested iterative structures, for is your best bet.

assoc_with_index = fn changeset, assoc ->
  changeset
  |> Ecto.Changeset.get_assoc(assoc)
  |> Enum.with_index()
end

source = socket.assigns.form.source

for {mockup, mockup_index} <- assoc_with_index.(source, :mockups),
    {element, element_index} <- assoc_with_index.(mockup, :elements),
    {_transformation, transformation_index} <- assoc_with_index.(element, :transformations) do
  allow_image_upload(
    socket,
    "transformation-#{mockup_index}-#{element_index}-#{transformation_index}"
  )
end
LostKobrakai

LostKobrakai

It also supports reduce, to allow wiring the socket state through.

sodapopcan

sodapopcan

Beautiful! So much nicer than what I was thinking.

Thank you both!

Last Post!

Sorc96

Sorc96

Personally, I probably wouldn’t bother looking into assoc_with_index, the usage makes pretty clear what it does. I’m also not saying that I would necessarily rewrite the code like this, I just wanted to show that the reduce was causing the most complexity here, in my opinion.

I guess my issue with the original code is that you need to thread the socket through all the layers in order to use it in the innermost reduce. That complicates it a lot for me, because the creation of the names and modification of the socket are mixed together. So maybe a version with a few flat_maps that generate the values first and then a reduce - basically my for example, but without the for - would be a pretty good solution as well.

Where Next?

Popular in Questions Top

Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
Emily
I have VueJS GUIs with the project generated using Webpack. I have Elixir modules that will need to be used by the VueJS GUIs. I forese...
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
komlanvi
Hi everyone, I was playing with phoenix liveView but I run into an issue. I have a form and want to validate each input text when the te...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New

Other popular topics Top

hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
dogweather
I wrote this comment on r/haskell, and it’s not popular there. :wink: But I think I’m on to something… Haskell reminds me of Java, and e...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
985 44778 311
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New

We're in Beta

About us Mission Statement