sodapopcan
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.
Trending in Questions
Other Trending 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
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #blog-post
- #elixir-ls
- #ai
- #elixirconf-us
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming










Showing Posts 1 to 10- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
100phlecs
When you start dealing with nested iterative structures,
foris your best bet.LostKobrakai
It also supports
reduce, to allow wiring the socket state through.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:sodapopcan
Beautiful! So much nicer than what I was thinking.
Thank you both!
Sorc96
This brings me joy! The
forcomprehension is so nice.D4no0
I would also consider an approach where I would break this down into small functions, so the main function eould be flat.
sodapopcan
I would do that if this became a common pattern. As it stands this is the only place in the app where I’m needing to index associations in order to enable uploads and this logic is already inside a private function, so I don’t want to break things up too much. Part of me actually still prefers my original example because it’s so stupidly simple, but I do appreciate the Credo rule and if I allow myself to up the nesting limit “just this one time”, there will definitely be several future instances of “Ok, this time is for sure the last time!”—I’m weak so I prefer to stick with defaults
dimitarvp
FWIW the
forvariant, while shorter, is absolutely opaque and is making it pretty hard to mentally parse and understand what is going on.I see nothing wrong with your original function but if you really have to act I would either (a) add a Credo exception or (b) make two sub-functions – corresponding to your two sub-levels.
I’d opt for sub-functions. Granted they’ll ruin the nice readability your original long function has but hey, if they are directly below it it should be pretty straightforward to read and grok.
sodapopcan
Ya, I might revert to my original version and completely kill the Credo rule as indenting too deeply isn’t something that happens often (I’m ok with outright removal of linting rules). The more I think about it, it’s really a pretty useless rule unless you are working with an army of developers you can’t trust. My other thought was that this feels like code that someone in the future will probably come along and rewrite anyway
I actually think the
forversion is quite readable and certainly very “pretty”, but I agree the original is more readable even if “ugly”. I do actually always useforwhenever I need a Cartesian product of two collections but for some reason as soon as I needed the product of three I was like, “duhhhhhhhhhhh what do I do better ask the internet!”. Maybe that is good signal for me not to use it here, haha. Not to **** on anyone who prefers thefor!Anyway, one thing is for sure that I will definitely choose one of these options hopefully without thinking too much more about it

Thanks for the input, all!
dimitarvp
I agree that
forcan be slightly “prettier” than a chain ofEnum.reduce-s. If you find that’s the case as well then I would try and make the sub-function version and just compare side by side. Informed choice and all.But yeah, I wasn’t claiming what I said as a fact, I was stating my quick intuitive impression. I learned to trust my gut so I am sharing what would I do.
Obviously go for the variant that makes it least likely to hate yourself when you revisit this code in the future.