Message Boards Message Boards


Can I have an un-Unionised Complement?

Posted 4 years ago
10 Replies
15 Total Likes

Hi, I have cats, fish and dogs.

In[173]:= animals = {cat, dog , fish, dog, cat}
Out[173]= {cat, dog, fish, dog, cat}

I want to take out the fish.

In[174]:= Complement[animals, {fish}]
Out[174]= {cat, dog}

Unfortunately Mathamatica killed one of my cat and one of my dog. I want a result that looks like:

Out[173]= {cat, dog, dog, cat}

or if Mathematica really wants to sort it, then:

Out[173]= {cat, cat, dog, dog}

How can I do it and make it fast?

Thanks ahead, János

P.S. The documentation says only that the result is sorted.

The list returned by Complement is sorted into standard order.

Well, it is not.

In[175]:= Sort[animals]
Out[175]= {cat, cat, dog, dog, fish}

Sort does not eliminate duplicates. Complement is Union-ized. Very bad for my purpose.

10 Replies
In[1]:= DeleteCases[{cat, dog, fish, dog, cat}, fish]

Out[1]= {cat, dog, dog, cat}

In[2]:= DeleteCases[{cat, dog, fish, dog, cat}, Alternatives @@ {cat, fish}]

Out[2]= {dog, dog}
Posted 4 years ago

Thanks, but no good.

In[3]:= DeleteCases[{cat, dog, fish, dog, cat}, cat]
Out[3]= {dog, fish, dog}

As you can see it deleted both cats. Well, in the meantime I solved my problem with Drop. Unfortunately it is not as fast as Complement.

animals = {cat, dog, fish, dog, cat};
In[7]:= Drop[animals, First[Position[animals, cat]]]
Out[7]= {dog, fish, dog, cat}

I am working with time series where the same values just cannot be eliminated, because that falsifies the results.

If your list contains sets of identical items, and you want just one of them dropped, which one is it that you want dropped? So this is much like you did above. If it is the first occurrence, we can just find its position in the list and drop it:

In[1]:= list = {dog, dog, cat, fish , cat, dog}

Out[1]= {dog, dog, cat, fish, cat, dog}

In[2]:= dropFrom[list_, thing_] := 
 Drop[list, Position[list, thing, 1, 1] // Flatten]

In[3]:= dropFrom[list, cat]

Out[3]= {dog, dog, fish, cat, dog}

In[4]:= dropFrom[list, bear]

Out[4]= {dog, dog, cat, fish, cat, dog}

As you can see it deleted both cats. Well, in the meantime I solved my problem with Drop.

That's fine for a single element, of course DeleteCases can also handle it:

  In[2]:= DeleteCases[{cat, dog, fish, dog, cat}, cat, {1}, 1]

  Out[2]= {dog, fish, dog, cat}

I am always overlooking the 3rd argument! :-)

Posted 4 years ago

Thanks guys, The major reason I wrote in the first place was the misstatement in the documentation that Complement does a sort. If it would have done a Sort, everything would be just fine, because Sort does not eliminate duplicates. Yes, I always want to drop the first one because of the time dependence. Drop, Delete and DeleteCases are taking more time than Complement. In the case of big lists that is significant.

What the documentation says is accurate, as the returned elements are indeed in canonical order.

  In[1]:= Complement[{c, b, a, c}, {c}]

  Out[1]= {a, b}

  In[2]:= % === Sort[%]

  Out[2]= True

The functions Union, Complement and Intersection are designed to work as the usual set-theoretic union, complement and intersection if the list arguments are considered as sets (which means repeated elements are ignored). This is explicitly documented for Union and Intersection. For example,

In[3]:= Intersection[{b, a, a, a}, {b, a, a}]

Out[3]= {a, b}

and again the result is sorted.

Just as an idea, something that also considers repetitions in the list of searched items:

deleteCasesOnce[list_List, cases_List] := Module[{countq},
   countq[_] := 0;
   Scan[(countq[#] = countq[#] + 1;) &, cases];
   Reap[If[countq[#] === 0, Sow[#], countq[#] = countq[#] - 1] & /@ list][[2, 1]]

And this returns what you expect:

In[]:= deleteCasesOnce[{cat, dog, fish, dog, cat, dog, cat, fish}, {cat, fish, cat}]
Out[]= {dog, dog, dog, cat, fish}

For multiple elements deletion, here are some fast implementation:

Removing elements from a list which appear in another list.

If you like terse coding (not so fast really), there is a closely related topic:

In[]:=   Fold[ DeleteCases[ ##, 1, 1]&, {cat, dog, fish, dog, cat, dog, cat, fish}, {cat, fish, cat}]
Out[]:=  {dog, dog, dog, cat, fish}
Posted 8 months ago

I have also had need for a non-Unionized Complement and Wolfram should consider adding such a function.

Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
or Discard

Group Abstract Group Abstract