One, pretty straight-forward, way to accomplish what you’re after is to think in the steps of what you want.
- You want to group your list into keys and the values of each unique key
- You want to count the unique values (in your example, you want to count the
1
s)
So you have something like this:
[%{x: :a, y: 1}, %{x: :a, y: 1}, %{x: :b, y: 0}, %{x: :b, y: 1}]
|> Enum.group_by(fn %{x: x} -> x end, fn %{y: y} -> y end)
|> IO.inspect(label: "grouped")
|> Enum.reduce([], fn {key, values}, acc ->
acc ++ [key, Enum.count(values, fn x -> x == 1 end)]
end)
Now you may think this is very specific to finding the count of 1s
. And you’re right.
To make it more general is just as easy.
Just think of the steps you need to take to get what you want.
It’s exactly as above with an extra step of finding the values of n
. …and use some functions to be kind to others.
defmodule Playground do
def hello do
[%{x: :a, y: 1}, %{x: :a, y: 1}, %{x: :b, y: 0}, %{x: :b, y: 1}]
|> count_values()
|> count_value(1)
|> IO.inspect(label: "count of 1s by key")
end
defp count_values(list) do
list
|> Enum.group_by(fn %{x: x} -> x end, fn %{y: y} -> y end)
|> IO.inspect(label: "values grouped by key")
|> Enum.reduce([], fn {key, values}, acc ->
counts =
Enum.reduce(values, %{}, fn value, acc ->
Map.update(acc, value, 1, &(&1 + 1))
end)
acc ++ [{key, counts}]
end)
|> IO.inspect(label: "values counted")
end
defp count_value(list, n) do
Enum.reduce(list, [], fn {key, values}, acc ->
count =
values
|> Enum.filter(fn {value, _} -> value == n end)
|> Enum.map(fn {_, count} -> count end)
|> IO.inspect(label: "#{key} #{n}s")
# ensure we use zero if we have no results in `count`
count =
case count do
[n] -> n
_ -> 0
end
acc ++ [{key, count}]
end)
end
end
Now that you have your answers, clean it up however you feel is necessary.
Cheers!