# Grouping and extract values from list of maps

Hi,
I have a list of maps such as:

``````[%{x: a, y: 1}, %{x: a, y: 1}, %{x: b, y: 0}, %{x: b, y: 1}]
``````

From this I would like to find out how many 1s a and b have respectively

ie. resulting in something like:

``````[a: 2, b:1]
``````

(or something to that effect)

I have accomplished this using rather brute force and un-elixirish ways and do feel there should be a rather elegant solution but it has eluded me so far and any help is appreciated.

Are `a` and `b` variables? Your given code snippet is not valid Elixir otherwise. They could be atoms (`:a` and `:b`) or strings (`"a"` and `"b"`).

2 Likes

What did you try already? What felt less-than-ideal to you about it?

Iâ€™d probably just start with an `Enum.reduce` and go from there, because this is pretty similar conceptually to the â€ścount the occurrence of a letter or word in a stringâ€ť problem that frequently appears in coding koans/challenges/exercises.

3 Likes

One, pretty straight-forward, way to accomplish what youâ€™re after is to think in the steps of what you want.

1. You want to group your list into keys and the values of each unique key
2. 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!

1 Like

No. I simply meant something that there can be more than one of on which I need to sort. In the original data they are the numbers 1 and 2, which also would make a confusing question here. I should have pointed this out.

Iâ€™m not hugely experienced in Elixir yet and my (bad) solution is basically splitting it into two separate lists, transforming them and merging back together. You really donâ€™t want to see the code

Now I feel rather embarrassed. I simply missed that there was a count/2. In hindsight this is rather obvious in language like this.

2 Likes