Get most common element in list

Hey everyone!

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

So if all items are equally often, you want to print, and do nothing, but what on this? [:a, :a, :b, :b, :c]? How do you wan’t to handle this?

Also your elses body can be a little more idiomaticly written as:

{item, count} = Enum.reduce(occurences_map, {0, 0}, fn
  {_, c1} = x, {_, c2} when c1 > c2 -> x
  _, acc -> acc
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.

1 Like

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. :slight_smile:

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. :slight_smile:

Is it statistical mode?

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

Average.mode([3,1,1,1,1,1,1,3,3,3,3,2,4])
[1]