48

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!

user64494
  • 26,149
  • 4
  • 27
  • 56
PeriodicProgrammer
  • 1,357
  • 1
  • 12
  • 15

10 Answers10

60

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.

Leonid Shifrin
  • 114,335
  • 15
  • 329
  • 420
  • thank you a lot, I do have V.8 and it works perfectly! Thank you for the fast answer! – PeriodicProgrammer Feb 04 '12 at 09:07
  • Thanks for including version-specific information; if you had not I would be left guessing. – Mr.Wizard Feb 04 '12 at 09:38
  • Thanks a lot, this works for any nested list! :) Great solution! – Lady InRed Oct 05 '12 at 09:58
  • @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 – Leonid Shifrin Oct 05 '12 at 14:24
  • @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? – matheorem Nov 21 '13 at 13:37
  • @LeonidShifrin and I found that though Complement sort result, but it's even faster then DeleteCases when I test large list! – matheorem Nov 21 '13 at 13:52
  • @matheorem These are not the questions to be answered in comments, and also I have zero time now. Search the site, the answers are there. – Leonid Shifrin Nov 21 '13 at 16:35
  • @LeonidShifrin This is great. How can I read the docs to imply that DeleteCases is guaranteed not to reorder (aside from possible flattening)? Is this guarantee explicit? – Alan Oct 29 '18 at 16:42
  • @Alan If you mean reodering the remaining entries in the first list, then I think DeleteCases does guarantee that this will not happen. Not sure how well that is expressed in the docs though. – Leonid Shifrin Oct 29 '18 at 18:02
30

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.

Yves Klett
  • 15,383
  • 5
  • 57
  • 124
  • 1
    Uh, Leonid beat me here. Should I leave the answer here anyway? – Yves Klett Feb 04 '12 at 09:05
  • 3
    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). – Leonid Shifrin Feb 04 '12 at 09:10
  • yes this is true. I found Complement on google, but I need the result to be unsorted. – PeriodicProgrammer Feb 04 '12 at 09:50
  • 3
    @PeriodicProgrammer Next time, please remember to include what you've tried and your constraints (unsorted), so that people don't waste time suggesting that. – rm -rf Feb 04 '12 at 13:25
21

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

DeleteDuplicates[#2 ~Join~ #] ~Drop~ Length[#2] &[list1, list2]
Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
18

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}).

rm -rf
  • 88,781
  • 21
  • 293
  • 472
13

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.

István Zachar
  • 47,032
  • 20
  • 143
  • 291
7

Just nest it:

Fold[DeleteCases[#1, #2] &, list1, list2]
RunnyKine
  • 33,088
  • 3
  • 109
  • 176
Fortsaint
  • 2,060
  • 15
  • 17
4

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}
pudepied
  • 246
  • 1
  • 5
3
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.

user688486
  • 485
  • 1
  • 7
3

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}

eldo
  • 67,911
  • 5
  • 60
  • 168
1

Using Cases:

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

Cases[list1, x_ /; FreeQ[x, Alternatives @@ list2] :> x]

{b, a, d}

E. Chan-López
  • 23,117
  • 3
  • 21
  • 44