otuv
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.
Marked As Solved
factoryd
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
1s)
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!
Also Liked
shanesveller
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.
dimitarvp
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").
otuv
Now I feel rather embarrassed. I simply missed that there was a count/2. In hindsight this is rather obvious in language like this.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









