michaelb
Filter list of maps
Hello!
I’m trying to filter list of maps to remove items where value of key was declared previously in this list. List that I have:
list = [%{
"distance" => "4",
"source" => "source_1",
},
%{
"distance" => "2",
"source" => "source_1",
},
%{
"distance" => "5",
"source" => "source_2",
},
%{
"distance" => "1",
"source" => "source_2",
},
%{
"distance" => "2",
"source" => "source_2",
}]
List what I want to get:
[%{
"distance" => "4",
"source" => "source_1",
},
%{
"distance" => "5",
"source" => "source_2",
}]
I tried next code:
sources = []
list
|> Enum.filter(fn p ->
if p["source"] in sources do
false
else
sources ++ p["source"]
true
end
end) |> IO.inspect
But looks like list “sources” not accessed from Enum.filter. Probably there exist more correct solution for this?
Marked As Solved
amnu3387
There’s Enum.uniq_by/2 that you can use, as in Enum.uniq_by(list, fn(%{"source" => s}) -> s end)
The reason your filter doesn’t work is because you’re building the list as you enumerate the elements and you can’t mutate the variable sources. You can achieve what you want with a reduction, where you keep track of them as you exemplified:
{_, new_list} = Enum.reduce(list, {[], []}, fn(%{"source" => s} = el, {sources, acc}) ->
if(s in sources, do: {sources, acc}, else: {[s | sources], [el | acc]})
end)
final = :lists.reverse(new_list)
Also Liked
vlarok
def single(list) do
Enum.uniq_by(list, &(%{"source" => &1.source}))
end
kokolegorille
Here is my one line ugly try 
iex> list |> Enum.reduce(%{}, fn %{"source" => s} = x, acc -> Map.put_new(acc, s, x) end) |> Enum.map(fn {_k, v} -> v end)
[
%{"distance" => "4", "source" => "source_1"},
%{"distance" => "5", "source" => "source_2"}
]
I use Map.put_new to ensure only first source is persisted
OvermindDL1
For note, your sources ++ p["source"] expression is evaluated here but the result is thrown away since it’s not being returned.
And it looks like you aren’t just wanting a unique one based on the name, but also the one with the highest value in each set, thus I’d probably do either this:
Enum.group_by(list, & &1["source"]) |> Enum.map(fn {_, v} -> Enum.max_by(v, & &1["distance"]) end)
Or this:
Enum.sort_by(list, & {&1["source"], &1["distance"]}, &>/2) |> Enum.dedup_by(& &1["source"])
Or any of a variety of other ways. ![]()







