Hello.
I want to iterate over the collection (array) where each element is a map containing key-value pairs. In there, I am interested in a key called like_type that can be true or false. What I want is to make a counter of how many true and false atoms are within this array.
I have made this function that should work in Javascript (haha), and I need help translating this into Elixir.
def get_number_of_votes(all_likes) do
upvote_count = 0
downvote_count = 0
for like <- all_likes do
case like.like_type do
true ->
upvote_count = upvote_count + 1
false ->
downvote_count = ^downvote_count + 1
_ ->
IO.puts("No true or false here.")
end
end
res = %{upvote_count: upvote_count, downvote_count: downvote_count}
IO.inspect(res)
res
end
Issue here is that I get this error:
“show_component.ex:13: cannot use ^downvote_count outside of match clauses”
This is what I get rendered within my liveview in Phoenix framework.
Thank you for the response!
Seems like the way we do this type of problem in Elixir is to flirt with Enum structures. Lots of ceremony there. I need to investigate more about the reduce method because it seems that it does the job elegantly.
all_likes
|> Enum.filter(&Map.has_key?(&1, :like_type))
|> Enum.reduce(%{upvote_count: 0, downvote_count: 0}, fn %{like_type: like_type}, acc ->
if like_type do
Map.put(acc, :upvote_count, acc.upvote_count + 1)
else
Map.put(acc, :downvote_count, acc.downvote_count + 1)
end
end)
You’ll generally need to reach for reduce when you’re iterating over a list or map. It can be thought of as iterating over each element and condensing the elements down to a single value which is returned as the result of the reduce operation. In this case, it is a map with the upvote and downvote counts
what were you hoping to happen when you wrote ^downvote_count?
writing some_existing_variable = "new_value" inside a block does not do what you want - it rebinds the name inside the block, but does not change the original binding outside
If you’re using Enum functions, a short way to write this is just like you’d describe the process manually:
Yes. This seems like the way Elixir wants me to do this problem. Pipes and filters and reducers. It does the job and its more elegant. Thank you for the response! JohnnyCurran
Thanks for the reponse! @ al2o3cr
With : ^downvote_count I was hoping that I overwrite the variable I have declared in the outer scope. Basically, I was experimenting and left it out there. split_with is an interesting function, one more in my Elixir vocabulary!
You can make use of Enum.frequencies_by/2, which will return a map that you can then extract the true and false counts to put into a new map as the upvoted and downvoted counts: