Message Boards Message Boards

Can I have an un-Unionised Complement?

GROUPS:

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.

POSTED BY: Janos Lobb
Answer
3 years ago
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 BY: Ilian Gachevski
Answer
3 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.

POSTED BY: Janos Lobb
Answer
3 years ago

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}
POSTED BY: David Keith
Answer
3 years ago

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}
POSTED BY: Ilian Gachevski
Answer
3 years ago

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

POSTED BY: David Keith
Answer
3 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.

POSTED BY: Janos Lobb
Answer
3 years ago

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.

POSTED BY: Ilian Gachevski
Answer
3 years ago

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}
Answer
3 years ago

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 BY: Kuba Podkalicki
Answer
3 years ago

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

POSTED BY: Bernard Gress
Answer
17 days ago

Group Abstract Group Abstract