49
$\begingroup$

I'm new to functional programming of Mathematica and trying to remove one list of assorted elements from another. However I only find functions working with the Index rather than the values itself:

 list1={b,a,e,f,c,d} list2={f,e,c} 

I can now remove list2 from list1:

 result={b,a,d} 

I already found out, that you can "abuse"

DeleteCases[list1, a] 

to remove 1 specific element from a list, but not a whole assorted list...

I would be very grateful for a simple solution to do it.
Thanks a lot for any answer!

$\endgroup$
1
  • 3
    $\begingroup$ Not functional, but just for fun: list1 /. Thread[list2 -> Nothing] $\endgroup$ Commented Apr 7, 2016 at 20:08

12 Answers 12

61
$\begingroup$

Use

DeleteCases[list1, Alternatives @@ list2] 

In new versions (M8.0+), DeleteCases is optimized on patterns not involving blanks, so this will be fast also for large lists. For earlier versions, this will work:

Replace[list1, Dispatch[Thread[list2 -> Sequence[]]],{1}] 

being 2-3 times slower, but still very fast.

$\endgroup$
9
  • $\begingroup$ thank you a lot, I do have V.8 and it works perfectly! Thank you for the fast answer! $\endgroup$ Commented Feb 4, 2012 at 9:07
  • $\begingroup$ Thanks for including version-specific information; if you had not I would be left guessing. $\endgroup$ Commented Feb 4, 2012 at 9:38
  • $\begingroup$ Thanks a lot, this works for any nested list! :) Great solution! $\endgroup$ Commented Oct 5, 2012 at 9:58
  • $\begingroup$ @AnastasiiaAnishchenko No problem :-). Actually, it only works on level 1. You probably meant that elements can be anything, including being themselves lists. Actually, I have developed a more general set of routines for similar kinds of operations, see the UnsortedOperations package on this page $\endgroup$ Commented Oct 5, 2012 at 14:24
  • $\begingroup$ @LeonidShifrin I am curious about the principle of DeleteCases. Why is it so fast than Select[list1, FreeQ[list2, #] &]? And I don't know the principle of Dispatch either. May be they have the same principle? $\endgroup$ Commented Nov 21, 2013 at 13:37
31
$\begingroup$

You are perhaps searching for Complement. Complement[list1, list2] results in {a, b, d}. The result is sorted though. If you are looking for an unsorted complement, DeleteCases[list1, Alternatives @@ list2] should probably work. I think there are some discussions on unsorted complements out there at google.

$\endgroup$
4
  • 1
    $\begingroup$ Uh, Leonid beat me here. Should I leave the answer here anyway? $\endgroup$ Commented Feb 4, 2012 at 9:05
  • 3
    $\begingroup$ Keep it, why not? I did not mention Complement (I actually implicitly assumed that the resulting list should not be sorted, which may or may not be the case). $\endgroup$ Commented Feb 4, 2012 at 9:10
  • $\begingroup$ yes this is true. I found Complement on google, but I need the result to be unsorted. $\endgroup$ Commented Feb 4, 2012 at 9:50
  • 3
    $\begingroup$ @PeriodicProgrammer Next time, please remember to include what you've tried and your constraints (unsorted), so that people don't waste time suggesting that. $\endgroup$ Commented Feb 4, 2012 at 13:25
22
$\begingroup$

Iff each list is internally free of duplicates you can use this very quick method:

DeleteDuplicates[#2 ~Join~ #] ~Drop~ Length[#2] &[list1, list2] 
$\endgroup$
2
  • 3
    $\begingroup$ Clever trick. +1. $\endgroup$ Commented Feb 4, 2012 at 9:30
  • $\begingroup$ This will keep list 1 in its original order—just what I was looking for! $\endgroup$ Commented Oct 10, 2020 at 15:40
19
$\begingroup$

Since Yves beat me narrowly to my first solution, here's another one using Select:

Select[list1, FreeQ[list2, #] &] Out[2]= {b, a, d} 

This does not sort your result.


You can also use Complement, which is more intuitive. Example:

Complement[list1, list2] Out[1]= {a, b, d} 

Note that this sorts your result (i.e., it is not in the same order, {b, a, d}).

$\endgroup$
13
$\begingroup$

Unsorted Complement. I think it originates from MathGroup. It accepts the SameTest option.

Options[UnsortedComplement] = {SameTest -> Automatic}; UnsortedComplement[all_, del___, opts : OptionsPattern[]] := Replace[all, List @@ (Rule[#, Sequence[]] & /@ Union[del, opts]), {1}]; all = RandomInteger[{0, 9}, {20}] UnsortedComplement[all, {6, 2, 8}, {2, 3, 4}] (* ==> {8, 4, 2, 5, 8, 8, 6, 6, 2, 1, 3, 1, 1, 2, 8, 0, 5, 5, 8, 1} ==> {5, 1, 1, 1, 0, 5, 5, 1} *) 

It also works with any head, not just List-s.

$\endgroup$
7
$\begingroup$

Just nest it:

Fold[DeleteCases[#1, #2] &, list1, list2] 
$\endgroup$
4
$\begingroup$

I hesitate to add to this old discussion after these excellent answers and other essential reading especially here, but this amalgamation of many suggestions may be of interest. cutEverything[xx_, removeList__] removes all entries of all elements of removeList_ from xx_ regardless of their depth or whether they are duplicates. It leaves compounds in place and does not change the original list order.

x = {pudepied, pudepied.txt, {pudepied}, {{{pudepied}}, {zap}, {{peter}}}, {{pudepied}}, blah, zap, "Wolfram Language", {peter}, {{{}}}, mary, {{{{mary}}}}}; remove = {pudepied, peter, mary, {}}; cutEverything[xx_, removeList_] := Replace[xx, x_List :> DeleteCases[x, Alternatives @@ removeList], {0, Infinity}] cutEverything[x, remove] 
 {pudepied.txt, {{zap}}, blah, zap, Wolfram Language} 
$\endgroup$
1
  • $\begingroup$ Thank you. Fixed! $\endgroup$ Commented Sep 28, 2020 at 10:09
4
$\begingroup$

To explore the preceding answer a little bit further:

list1 = {b, a, e, f, c, d}; list2 = {f, e, c}; DeleteElements[list1, list2] 

{b, a, d}

If list1 contains duplicated elements:

list1 = {b, a, e, f, c, c, d}; list2 = {f, e, c}; 

Delete all elements:

DeleteElements[list1, list2] 

{b, a, d}

Delete up to 1 element:

DeleteElements[list1, 1 -> list2] 

{b, a, c, d}

$\endgroup$
3
$\begingroup$
DeleteElements[{b, a, e, f, c, d}, \[Infinity] \[Rule] {f, e, c}] (*{b, a, d}*) 

Unfortunately, the new function DeleteElements is not faster:

In[1]:= list1 = RandomReal[1, 2*^4]; list2 = Catenate[{RandomChoice[list1, 6*^3], RandomReal[1, 4*^3]}]; In[2]:= result0=DeleteElements[list1,list2];//AbsoluteTiming Out[2]= {3.78426, Null} In[3]:= result1=DeleteCases[list1,Alternatives@@list2];//AbsoluteTiming Out[3]= {0.01786, Null} In[4]:= result2=Replace[list1,Dispatch[Thread[list2->Sequence[]]],{1}];//AbsoluteTiming Out[4]= {0.0342811, Null} In[5]:= result3=Replace[list1,x_List:>DeleteCases[x,Alternatives@@list2],{0,Infinity}];//AbsoluteTiming Out[5]= {0.0222806, Null} In[6]:= result4=Fold[DeleteCases[##]&,list1,list2];//AbsoluteTiming Out[6]= {24.3309, Null} In[7]:= Equal[result0,result1,result2,result3,result4] Out[7]= True 

Other ways are much slower, so I omit them here.

$\endgroup$
2
$\begingroup$

Using Cases:

list1 = {b, a, e, f, c, d}; list2 = {f, e, c}; Cases[list1, x_ /; FreeQ[x, Alternatives @@ list2] :> x] {b, a, d} 
$\endgroup$
2
  • 1
    $\begingroup$ Similar: Pick[list1, FreeQ[#, Alternatives @@ list2] & /@ list1] $\endgroup$ Commented Nov 18, 2023 at 7:58
  • $\begingroup$ It's a very good solution, @eldo! Thanks for pointing it out. :-) $\endgroup$ Commented Nov 18, 2023 at 20:30
1
$\begingroup$

If you care about repeated elements in list2, i.e. you want to emulate the behavior of

DeleteElements[list1, 1 -> list2] 

efficiently for long lists, using associations helps. Try something like:

{list1,list2}\ //Map[Tally/*Transpose/*(AssociationThread@@#&)]\ //MapAt[Minus,2]\ //Merge[Total]\ //Select[Positive]\ //Normal\ //Map[Table@@#&]\ //Join@@#& 
$\endgroup$
1
$\begingroup$

Using Sow/Reap:

list1 = {b, a, e, f, c, d}; list2 = {f, e, c}; Scan[If[MemberQ[list2, #], Nothing, Sow[#]] &, list1] // Reap // Last // First 

Using Gatherby:

GatherBy[list1, MemberQ[list2, #] &] // First 

{b, a, d}

$\endgroup$

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.