chokchit
How can I compare two list of maps?
Hey guys, just want a little help.
(1) How can I compare two list of maps and always supersedes list_2 over list_1 if they have the same name? and (2) return the map of list_1 if it doesn’t have a similar name?
Given:
list_1 = [%{id: 1, name: "John"}, %{id: 1, name: "Jane"}, %{school_id: 1, name: "Ryan"}, ...]
list_2 = [%{id: 2, name: "John"}, %{id: 2, name: "Jane"}, ...]
Expected result:
[
%{school_id: 2, name: "John"},
%{school_id: 2, name: "Jane"},
%{school_id: 1, name: "Ryan"},
...
]
Most Liked
imatmati
Enum.concat(list_2,list_1)
|> Enum.sort( &(&1.name<= &2.name))
|> Enum.dedup_by(&(&1.name))
6
amnu3387
Two other ways are using Enum.reduce/3 or a reduction function:
Enum.concat(Test.list_2(), Test.list_1())
|> Enum.reduce({[], %{}}, fn
(%{name: name}, {_, map_matches} = full_acc) when :erlang.is_map_key(name, map_matches) ->
full_acc
(%{name: name} = el, {acc, map_matches}) ->
{[el | acc], Map.put(map_matches, name, true)}
end)
|> elem(0)
Or
def build(list, acc \\ {[], %{}})
def build([], {acc, _}), do: acc
def build([%{name: name} | t], {_acc, map_matches} = full_acc) when :erlang.is_map_key(name, map_matches), do: build(t, full_acc)
def build([%{name: name} = el | t], {acc, map_matches}) do
build(t, {[el | acc], Map.put(map_matches, name, true)})
end
#test.exs
defmodule Test do
@list_1 Enum.reduce_while(Stream.cycle([0]), {0, []},
fn(_n, {count, acc}) when count < 25000 ->
{:cont, {count + 1, [%{id: count + 1, name: "a_#{count}_name"} | acc]}}
(_, {_, acc}) -> {:halt, acc}
end)
@list_2 @list_1 |> List.delete_at(10) |> List.delete_at(50) |> List.delete_at(100) |> List.delete_at(500) |> List.delete_at(750)
def list_1, do: @list_1
def list_2, do: @list_2
# reduce w/ function
def build(list, acc \\ {[], %{}})
def build([], {acc, _}), do: acc
def build([%{name: name} | t], {_acc, map_matches} = full_acc) when :erlang.is_map_key(name, map_matches), do: build(t, full_acc)
def build([%{name: name} = el | t], {acc, map_matches}) do
build(t, {[el | acc], Map.put(map_matches, name, true)})
end
def test do
test1 =
Enum.concat(Test.list_2(), Test.list_1())
|> Enum.reduce({[], %{}}, fn
(%{name: name}, {_, map_matches} = full_acc) when :erlang.is_map_key(name, map_matches) -> full_acc
(%{name: name} = el, {acc, map_matches}) ->
{[el | acc], Map.put(map_matches, name, true)}
end)
|> elem(0)
|> Enum.sort()
test2 =
:lists.flatten([Test.list_2() | Test.list_1()])
|> Enum.reduce({[], %{}}, fn
(%{name: name}, {_, map_matches} = full_acc) when :erlang.is_map_key(name, map_matches)-> full_acc
(%{name: name} = el, {acc, map_matches}) ->
{[el | acc], Map.put(map_matches, name, true)}
end)
|> elem(0)
|> Enum.sort()
test3 =
Map.new(Test.list_1(), &{&1.name, &1})
|> Map.merge(Map.new(Test.list_2(), &{&1.name, &1}))
|> Enum.map(&elem(&1, 1))
|> Enum.sort()
test4 =
Enum.concat(Test.list_2(), Test.list_1())
|> Enum.sort( &(&1.name<= &2.name))
|> Enum.dedup_by(&(&1.name))
|> Enum.sort()
test5 =
Enum.map(Test.list_1(), fn %{name: n} = m ->
Enum.find(Test.list_2(), m, &match?(%{name: ^n}, &1))
end)
|> Enum.sort()
test6 =
Enum.concat(Test.list_2(), Test.list_1())
|> Test.build()
|> Enum.sort()
IO.inspect(test1 == test2, label: "1 == 2")
IO.inspect(test1 == test3, label: "1 == 3")
IO.inspect(test1 == test4, label: "1 == 4")
IO.inspect(test1 == test5, label: "1 == 5")
IO.inspect(test1 == test6, label: "1 == 6")
end
end
defmodule TestTest do
Test.test()
end
Benchee.run(
%{
"Enum.reduce & concat" => fn ->
Enum.concat(Test.list_2(), Test.list_1())
|> Enum.reduce({[], %{}}, fn
(%{name: name}, {_, map_matches} = full_acc) when :erlang.is_map_key(name, map_matches) -> full_acc
(%{name: name} = el, {acc, map_matches}) ->
{[el | acc], Map.put(map_matches, name, true)}
end)
|> elem(0)
end,
"Enum.reduce & flatten" => fn ->
:lists.flatten([Test.list_2() | Test.list_1()])
|> Enum.reduce({[], %{}}, fn
(%{name: name}, {_, map_matches} = full_acc) when :erlang.is_map_key(name, map_matches)-> full_acc
(%{name: name} = el, {acc, map_matches}) ->
{[el | acc], Map.put(map_matches, name, true)}
end)
|> elem(0)
end,
"reduce function" => fn ->
Enum.concat(Test.list_2(), Test.list_1())
|> Test.build()
end,
"map_merge" => fn ->
Map.new(Test.list_1(), &{&1.name, &1})
|> Map.merge(Map.new(Test.list_2(), &{&1.name, &1}))
|> Enum.map(&elem(&1, 1))
end,
"sort_dedup" => fn ->
Enum.concat(Test.list_2(), Test.list_1())
|> Enum.sort( &(&1.name<= &2.name))
|> Enum.dedup_by(&(&1.name))
end,
"enum_find" => fn ->
Enum.map(Test.list_1(), fn %{name: n} = m ->
Enum.find(Test.list_2(), m, &match?(%{name: ^n}, &1))
end)
end
},
time: 2
)
Which gives:
1 == 2: true
1 == 3: true
1 == 4: true
1 == 5: true
1 == 6: true
Operating System: macOS"
CPU Information: Intel(R) Core(TM) i7-4750HQ CPU @ 2.00GHz
Number of Available Cores: 8
Available memory: 8 GB
Elixir 1.7.2
Erlang 21.0
Benchmark suite executing with the following configuration:
warmup: 2 s
time: 2 s
memory time: 0 μs
parallel: 1
inputs: none specified
Estimated total run time: 24 s
Benchmarking Enum.reduce & concat...
Benchmarking Enum.reduce & flatten...
Benchmarking enum_find...
Benchmarking map_merge...
Benchmarking reduce function...
Benchmarking sort_dedup...
Name ips average deviation median 99th %
reduce function 53.90 18.55 ms ±9.14% 19.02 ms 23.64 ms
Enum.reduce & flatten 51.34 19.48 ms ±9.26% 19.40 ms 23.77 ms
Enum.reduce & concat 50.97 19.62 ms ±8.30% 20.04 ms 22.77 ms
map_merge 46.92 21.31 ms ±7.83% 21.44 ms 25.13 ms
sort_dedup 43.35 23.07 ms ±6.16% 23.07 ms 26.70 ms
enum_find 0.0740 13515.56 ms ±0.00% 13515.56 ms 13515.56 ms
Comparison:
reduce function 53.90
Enum.reduce & flatten 51.34 - 1.05x slower
Enum.reduce & concat 50.97 - 1.06x slower
map_merge 46.92 - 1.15x slower
sort_dedup 43.35 - 1.24x slower
enum_find 0.0740 - 728.52x slower
The results change depending on the length of the lists, but the function is always a tad faster
4
gregvaughn
Enum.map(list_1, fn %{name: n} = m ->
Enum.find(list_2, m, &match?(%{name: ^n}, &1))
end)
2
peerreynders
iex(1)> list_1 = [%{id: 1, name: "John"}, %{id: 1, name: "Jane"}, %{id: 1, name: "Ryan"}]
[%{id: 1, name: "John"}, %{id: 1, name: "Jane"}, %{id: 1, name: "Ryan"}]
iex(2)> list_2 = [%{id: 2, name: "John"}, %{id: 2, name: "Jane"}]
[%{id: 2, name: "John"}, %{id: 2, name: "Jane"}]
iex(3)> map_2 = Map.new(list_2, fn %{name: name} = item -> {name, item} end)
%{"Jane" => %{id: 2, name: "Jane"}, "John" => %{id: 2, name: "John"}}
iex(4)> result =
...(4)> Enum.map(list_1, fn %{name: name} = item ->
...(4)> case Map.fetch(map_2, name) do
...(4)> {:ok, new_item} ->
...(4)> new_item
...(4)> _ ->
...(4)> item
...(4)> end
...(4)> end)
[%{id: 2, name: "John"}, %{id: 2, name: "Jane"}, %{id: 1, name: "Ryan"}]
iex(5)>
2
OvermindDL1
Another way:
iex(1)> list_1 = [%{id: 1, name: "John"}, %{id: 1, name: "Jane"}, %{school_id: 1, name: "Ryan"}]
[%{id: 1, name: "John"}, %{id: 1, name: "Jane"}, %{name: "Ryan", school_id: 1}]
iex(2)> list_2 = [%{id: 2, name: "John"}, %{id: 2, name: "Jane"}]
[%{id: 2, name: "John"}, %{id: 2, name: "Jane"}]
iex(3)>
nil
iex(4)> Map.merge(Map.new(list_1, &{&1.name, &1}), Map.new(list_2, &{&1.name, &1})) |> Enum.map(&elem(&1, 1))
[%{id: 2, name: "Jane"}, %{id: 2, name: "John"}, %{name: "Ryan", school_id: 1}]
I.E.:
Map.new(list_1, &{&1.name, &1})
|> Map.merge(Map.new(list_2, &{&1.name, &1}))
|> Enum.map(&elem(&1, 1))
2
Popular in Questions
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
Using vs code and installed ElixirLS: support and debugger.
And I got an error popped up on start up says
Failed to run ‘elixir’ comma...
New
Hi there, I'm working through my first release with elixir/phoenix. I've built a release with distillery and found that it crashes when I...
New
In Ruby, I can go:
User.find_by(email: "foobar@email.com").update(email: "hello@email.com")
How can I do something similar in Elixir? ...
New
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
What is the idiomatic way of matching for not nil in Elixir?
E.g.,
First way:
defp halt_if_not_signed_in(conn, signed_in_account) when...
New
I have followed this StackOverflow post to install the specific version of Erlang.
And When I am running mix ecto.setup then getting fol...
New
I have a User schema with a :from_id field set to type :string:
defmodule TweetBot.Repo.Migrations.CreateUsers do
use Ecto.Migration
...
New
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
Other popular topics
Hello!
tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability.
After spen...
New
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
Credo is smart enough to check for (something like) this:
assert length(the_list) == 0
with this response:
Checking if an enum is empt...
New
I have followed this StackOverflow post to install the specific version of Erlang.
And When I am running mix ecto.setup then getting fol...
New
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
Could someone help me? I'm making my first elixir program, number guessing game. I can't figure out how to convert the user's guess from ...
New
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch.
This project took far...
New
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible.
total = 10
while total != 0
...
New
Hi guys, i’m new in the Elixir world, and i have to say, that i love it!
i’m having some problem to understand anonymous functions with ...
New
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New







