How to Utilize Enum.any?, with a Refactoring Twist!
Tags: elixir, phoenix, thisweekilearned, learning
Published: 2020-06-16 14:30:36

This week I didn't have any mind-blowing revelations, but those are like unicorns, I'm fortunate when I come across them, but I know that it wont happen all the time. I did -through a colleague's PR comment- discover another Elixir function that will make my future life a little bit easier, [`Enum.any?/2`](https://hexdocs.pm/elixir/1.1.0/Enum.html#any?/2).

I was back in the world of custom validations, and they were getting more deeply nested than I liked, but I didn't see any way to make them more concise. 

{% gist https://gist.github.com/noelworden/72f255e315c45b1f847343a4cbb11d42 %}

There's a lot going on here, but let's focus on the case statements that are being executed. With this particular validation the error shown to the user is the same in the case of all the `false`s, in hindsight that's a clue that this could be cleaned up.

When it's all said and done I need to check if `total_merch_rev` minus `total_group_costs` equals `net_revenue_shared`. But the fields coming in are not in the exact same shape, and 0 != 0.00. I first have to explicitly check if the fields are zero, and if not, then check if `total_merch_rev` minus `total_group_costs` equals `net_revenue_shared`?

That logic would look something like this:

{% gist https://gist.github.com/noelworden/23e05772dc14c3aa546c239f618be815 %}

This is where `Enum.any?/2` comes into play, as it invokes the function for each item in the collection returning `true` if at least one invocation returns `true`, and `false` otherwise. Basically both of those chunks of logic can be dropped into that function, and if either return true, the changeset would be returned, and if either return false the error would be returned. So, the refactor would look something like this:

{% gist https://gist.github.com/noelworden/e6d430271e4c2324e0699d32c2781334 %}

The `rev_minus_cost == @zero_with_decimal && net_rev == @zero_with_decimal` line is equivalent to:

{% gist https://gist.github.com/noelworden/d16493e8f1732f9e175880df1f91aa83 %}

And, the `rev_minus_cost == net_rev` line covers this case:

{% gist https://gist.github.com/noelworden/f2978068e7f718bc81835b8a9b62e755 %}

But, as I write this Im seeing how this can be refactored _further_ and not even use `Enum.any?/2`! Basically, the problem I was originally having was how the data was coming in, versus the result of the mathematical logic. Again, it all came down to the fact that 0 != 0.00. But by rounding both the `total_merch_rev` minus `total_group_costs` result and `net_revenue_shared` data to the hundredth place, I'm essentially normalizing the data, and it can be compared to itself directly, even if the logic result or field is `0`.

{% gist https://gist.github.com/noelworden/332f377c273446b648ac638f50d35e6e %}

So, the result of that refactor is this:

{% gist https://gist.github.com/noelworden/455e145ede4be099684aac9ee7f26295 %}

Well, I have to say, this is pretty awesome. While revisiting past work to write about a function, I see a way to refactor that code even further while writing about it, and ultimately refactor it in a way that it doesn't even need the function that the post is highlighting. It's great that deeply nested logic can be cleaned up so efficiently with `Enum.any?/2` (along with the related function `Enum.all?/2`), but I'm actually pretty happy that I was able to removed the need for it in this particular example.

------

This post is part of an ongoing *This Week I Learned* [series](https://dev.to/noelworden/beginning-of-a-blog-series-5aj3). I welcome any critique, feedback, or suggestions in the comments.