2017-10-06 85 views
1

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

回答

2

你並不需要一個外部Enum.each,你可以用一個過濾器通過枚舉了a和檢查每個元素,看看它是否是b成員做到這一點:

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

輸出:

[1, 2] 

它看起來像當前的解決方案,你試圖修改a但不會工作,因爲藥劑是功能性的,功能不能有副作用; each中的a與原始列表中的a不一樣。

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,我做到了,請檢查結果。 –

0

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