I’m new to elixir and trying to figure out how to think the elixir way.
I want to get most common element from list as I will use it later, but I don’t know if I understand the way how it should be done. Is there a more efficient way to count the most common item in structure?
Here’s the code I wrote :
defmodule Sample do
@moduledoc """
Implements basic circle functions
"""
def most_common(list) do
occurencies_map = Enum.reduce(list, %{}, fn (x, acc) ->
Map.update(acc, x, 1, fn(x) -> x + 1 end) end)
if length(Enum.uniq(Map.values(occurencies_map))) == 1 do
IO.puts("All elements have same frequency in list" <> inspect(list))
else
popular = Enum.reduce(Map.to_list(occurencies_map), {0, 0}, fn
x, acc when elem(x, 1) > elem(acc, 1) -> x
_, acc -> acc
end)
IO.puts("Most common element is " <> inspect(elem(popular, 0)) <>
" with frequency " <> inspect(elem(popular, 1)))
end
end
end
defmodule Sample do
def most_common(list) when is_list(list) do
{amts, highest_count, highest_values} =
Enum.reduce(list, {%{}, 0, []}, fn(v, {a, hc, hv}) ->
case Map.update(a, v, 1, &(&1+1)) do
%{^v => ^hc}=a -> {a, hc, [v | hv]}
%{^v => c}=a when c>hc -> {a, c, [v]}
a -> {a, hc, hv}
end
end)
# Now we have all the interesting bits, the example code in the first post just prints though...
if map_size(amts) == length(highest_values) do
IO.puts("All elements have the same frequency in list: #{inspect list}")
else
IO.puts("Most common elements are #{inspect highest_values} with frequency #{highest_count}")
end
end
end
It’s probably one of the most efficient while returning the most information short of doing pure module-inline calls. Could make it more efficient if you did not want all the information it returns from the reduce.
OvermindDL1, NobbZ, Thanks for your reply!
Now I see what I have missed in the first place. OvermindDL1 your code is really what I needed! So elegant and understandable. Thanks, solved!
Hah, usually I consider my optimized code an optimized unreadable horror, thanks! ^.^
Although I bet it really would be if I optimized it into in-module calls, would be even faster then (not by much though) and more unreadable both.
You can mark a post as solved with the Solved button at the bottom of it too. Helps to mark it as such in search results and a pointer on the first post is automatically generated.
defmodule Average do
def mode(list) do
gb = Enum.group_by(list, &(&1))
max = Enum.map(gb, fn {_,val} -> length(val) end) |> Enum.max
for {key,val} <- gb, length(val)==max, do: key
end
end
taken from here: https://rosettacode.org/wiki/Averages/Mode#Elixir