Reputation: 77
I want to remove the elements found in list b from list a. The list a is printing [1,2,3,4] after execution of this code.
defmodule Test do
def listing do
a = [1,2,3,4]
b = [3,4,5,6]
Enum.each b, fn elemB ->
a = Enum.filter(a, fn(x) -> x != elemB == true end)
#IO.inspect a
end
IO.inspect a
end
end
Test.listing()
Upvotes: 4
Views: 7597
Reputation: 2965
I'm not an expert on elixir but you can try something like this
Enum.reject(a, fn x -> Enum.member?(b, x) end)
Upvotes: 0
Reputation: 114
In Elixir >= v1.4 there is the function List.myers_difference, so you can do
a = [1,2,3,4]
b = [3,4,5,6]
List.myers_difference(a,b) |>
Keyword.get(:del)
[1,2]
doc to List.myers_difference
https://hexdocs.pm/elixir/List.html#myers_difference/2
Upvotes: 2
Reputation: 2176
Following to @Tyler answer,
You can make your code clearer using Enum.reject
instead of Enum.filter
:
Enum.reject(a, fn el -> Enum.member?(b, el) end)
This will give the same result as:
Enum.filter(a, fn el -> !Enum.member?(b, el) end)
Upvotes: 1
Reputation: 9841
Both ways presented in other answers at the moment (Enum.filter
and --
) will work well on small lists. But, lists are large, they are very inefficient.
If lists are large, better to use MapSet
:
MapSet.difference(MapSet.new(a), MapSet.new(b)) |> MapSet.to_list
It spends some time to convert both lists to MapSets, and then to convert result back to list, but these operations are n log(n)
, while Enum.filter
and substraction (--
) here are quadratic.
I've prepared a gist with benchmarks.
Summary: for very short list substraction is fastest, for lists around 100 elements long Enum.filter
is fastest, and for lists around 1000 elements MapSet.difference
is fastest. It is hundreds times faster on lists with 100K elements.
Actually, on lists this size MapSet.difference
works 0.08 seconds, Enum.filter
16 seconds, and substraction 44 seconds.
UPDATE: Dogbert
asked me to also benchmark Erlang's ordsets
:
:ordsets.subtract(:ordsets.from_list(a), :ordsets.from_list(b)) |> :ordsets.to_list
It works faster than MapSet
, especially on mid-size lists about 1000 records long (MapSet
is about 1.4
times slower).
Upvotes: 16
Reputation: 4885
You can also use the --
operator (https://hexdocs.pm/elixir/Kernel.html#--/2)
iex> [1, 2, 3] -- [1, 2]
[3]
Upvotes: 14
Reputation: 18177
You don't need that outer Enum.each
, you can do it with a single filter by enumerating over a
and checking each element to see if it is a member of b
:
Enum.filter(a, fn el -> !Enum.member?(b, el) end)
Outputs:
[1, 2]
It looks like with your current solution you are trying to modify a
but that won't work because Elixir is functional and the function can't have side effects; the a
inside your each
is not the same as the a
that is the original list.
Upvotes: 4