我想从列表a中删除列表b中的元素。 列表a在执行此代码后打印[1,2,3,4]。
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()
答案 0 :(得分:9)
目前在其他答案(Enum.filter
和--
)中提供的两种方式都适用于小型列表。但是,列表很大,效率非常低。
如果列表很大,最好使用MapSet
:
MapSet.difference(MapSet.new(a), MapSet.new(b)) |> MapSet.to_list
花费一些时间将两个列表转换为MapSet,然后将结果转换回列表,但这些操作是n log(n)
,而Enum.filter
和减法(--
)这里是二次的。
我准备了gist with benchmarks。
总结:对于非常短的列表,减法是最快的,对于大约100个元素的列表,Enum.filter
是最快的,对于列表大约1000个元素MapSet.difference
是最快的。在具有100K元素的列表上,它快了数百倍。
实际上,在列表中,此大小MapSet.difference
的工作时间为0.08秒,Enum.filter
为16秒,减法为44秒。
更新:Dogbert
让我也对Erlang ordsets
进行基准测试:
:ordsets.subtract(:ordsets.from_list(a), :ordsets.from_list(b)) |> :ordsets.to_list
它比MapSet
工作得更快,特别是在中等大小的列表上,大约有1000条记录(MapSet
大约慢1.4
倍)。
答案 1 :(得分:8)
您还可以使用--
运算符(https://hexdocs.pm/elixir/Kernel.html#--/2)
iex> [1, 2, 3] -- [1, 2]
[3]
答案 2 :(得分:2)
您不需要外部Enum.each
,您可以通过枚举a
并检查每个元素以查看它是否是b
的成员来使用单个过滤器执行此操作:
Enum.filter(a, fn el -> !Enum.member?(b, el) end)
输出:
[1, 2]
使用您当前的解决方案,您试图修改a
,但这不起作用,因为Elixir功能正常,并且该功能不会产生副作用; a
中的each
与原始列表中的a
不同。
答案 3 :(得分:1)
跟@Tyler回答,
您可以使用Enum.reject
代替Enum.filter
:
Enum.reject(a, fn el -> Enum.member?(b, el) end)
这将得到与结果相同的结果:
Enum.filter(a, fn el -> !Enum.member?(b, el) end)
答案 4 :(得分:1)
在Elixir> = v1.4中,具有List.myers_difference函数,因此您可以这样做
a = [1,2,3,4]
b = [3,4,5,6]
List.myers_difference(a,b) |>
Keyword.get(:del)
[1,2]
doc到List.myers_difference
答案 5 :(得分:0)
我不是长生不老药的专家,但是您可以尝试这样的事情
Enum.reject(a, fn x -> Enum.member?(b, x) end)