Elixir从两个列表中删除常用元素

时间:2017-10-06 22:51:27

标签: elixir

我想从列表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()

6 个答案:

答案 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

https://hexdocs.pm/elixir/List.html#myers_difference/2

答案 5 :(得分:0)

我不是长生不老药的专家,但是您可以尝试这样的事情

Enum.reject(a, fn x -> Enum.member?(b, x) end)