Elixir从两个列表中删除公共元素

问题描述:

我想从列表a中删除列表b中找到的元素。 执行此代码后,列表a正在打印[1,2,3,4]。Elixir从两个列表中删除公共元素

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() 

你并不需要一个外部Enum.each,你可以用一个过滤器通过枚举了a和检查每个元素,看看它是否是b成员做到这一点:

Enum.filter(a, fn el -> !Enum.member?(b, el) end) 

输出:

[1, 2] 

它看起来像当前的解决方案,你试图修改a但不会工作,因为药剂是功能性的,功能不能有副作用; each中的a与原始列表中的a不一样。

您还可以使用--运营商(https://hexdocs.pm/elixir/Kernel.html#--/2

iex> [1, 2, 3] -- [1, 2] 
[3] 

的时刻(Enum.filter--)将在小名单的工作以及在其他的答案提出的两种方式。但是,清单很大,效率很低。

如果名单是大,最好使用MapSet

MapSet.difference(MapSet.new(a), MapSet.new(b)) |> MapSet.to_list 

它花费一些时间来两个列表转换为地图集,然后结果转换回列表,但是这些操作都是n log(n),而Enum.filter和这里的减法(--)是二次的。我准备了gist with benchmarks

摘要:对于非常短的列表减法是最快的,对于大约100个元素长的列表Enum.filter是最快的,对于列表大约1000个元素MapSet.difference是最快的。在具有100K元素的列表上,它的速度要快上百倍。

其实在列表上这个大小MapSet.difference工作0.08秒,Enum.filter 16秒,减去44秒。

UPDATEDogbert问我还基准Erlang的ordsets

:ordsets.subtract(:ordsets.from_list(a), :ordsets.from_list(b)) |> :ordsets.to_list 

它比MapSet快,特别是在中型列出大约1000条记录长(MapSet慢约1.4倍)。

+0

难道你还可以添加' ordsets'到基准? Erlang文档建议使用它而不是'--'。 ':ordsets.subtract(:ordsets.from_list(a),:ordsets.from_list(b))|>:ordsets.to_list'。 – Dogbert

+1

另外,List to MapSet转换不是线性的,而是O(n log n),因为在Erlang中的Map中插入值是O(log n)。 – Dogbert

+1

@Dogbert,我做到了,请检查结果。 –

按照与@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)