views:

171

answers:

11

I have a third party api, which has a class that returns an enumerator for different items in the class.

I need to remove an item in that enumerator, so I cannot use "for each". Only option I can think of is to get the count by iterating over the enum and then run a normal for loop to remove the items.

Anyone know of a way to avoid the two loops?

Thanks

[update] sorry for the confusion but Andrey below in comments is right.

Here is some pseudo code out of my head that won't work and for which I am looking a solution which won't involve two loops but I guess it's not possible:

for each (myProperty in MyProperty)
{
if (checking some criteria here)
   MyProperty.Remove(myProperty)
}

MyProperty is the third party class that implements the enumerator and the remove method.

+2  A: 

Loop through it once and create a second array which contains the items which should not be deleted.

Sjoerd
**Should** or **should not**? You should consider mentioning what you'd do with that array once you've got it.
Neil Barnwell
Seems like the second array becomes the end result.
phasetwenty
+6  A: 

Common pattern is to do something like this:

List<Item> forDeletion = new List<Item>();

foreach (Item i in somelist)
   if (condition for deletion) forDeletion.Add(i);

foreach (Item i in forDeletion)
   somelist.Remove(i); //or how do you delete items 
Andrey
Well I wanted to avoid looping twice. Perhaps if it was a collection instead of an enumerator then just one for loop would have sufficed?
pug
@pug just try. create a simple list and remove item while iterating.
Andrey
+2  A: 

If you know it's a collection, you can go with reverted for:

for (int i = items.Count - 1; i >= 0; i--)
{
   items.RemoveAt(i);
}

Otherwise, you'll have to do two loops.

František Žiačik
+1  A: 

It is not possible to remove an item from an Enumerator. What you can do is to copy or filter(or both) the content of the whole enumeration sequence. You can achieve this by using linq and do smth like this:

   YourEnumerationReturningFunction().Where(item => yourRemovalCriteria);
Koynov
+1  A: 

Can you elaborate on the API and the API calls you are using?

If you receive an IEnumerator<T> or IEnumerable<T> you cannot remove any item from the sequence behind the enumerator because there is no method to do so. And you should of course not rely on down casting an received object because the implementation may change. (Actually a well designed API should not expose mutable objects holding internal state at all.)

If you receive IList<T> or something similar you can just use a normal for loop from back to front and remove the items as needed because there is no iterator which state could be corrupted. (Here the rule about exposing mutable state should apply again - modifying the returned collection should not change any state.)

Daniel Brückner
Exactly the point of my comment above - he's talking about enumerators and everyone's assuming he's got a different interface and is answering a different question. Of course they might be right, but the OP hasn't confirmed either way as yet.
Neil Barnwell
+2  A: 

You can create something like this:

      public IEnumerable<item> GetMyList()
    {
        foreach (var x in thirdParty )
        {
            if (x == ignore)
                continue;
            yield return x;
        }

    }
Matin Habibi
that would be more effective as a LINQ function IMHO: return thirdParty.Where(x => x != ignore);
dbemerlin
A: 

IEnumerator.Count() will decide at run-time what it needs to do - enumerate to count or reflect to see it's a collection and call .Count that way.

I like SJoerd's suggestion but I worry about how many items we may be talking about.

n8wrl
A: 

Why not something like ..

 // you don't want 2 and 3
 IEnumerable<int> fromAPI = Enumerable.Range(0, 10);
 IEnumerable<int> result = fromAPI.Except(new[] { 2, 3 });
Moberg
A: 

A clean, readable way to do this is as follows (I'm guessing at the third-party container's API here since you haven't specified it.)

foreach(var delItem in ThirdPartyContainer.Items
                       .Where(item=>ShouldIDeleteThis(item))
                       //or: .Where(ShouldIDeleteThis)
                       .ToArray()) {
    ThirdPartyContainer.Remove(delItem);
}

The call to .ToArray() ensures that all items to be deleted have been greedily cached before the foreach iteration begins.

Behind the scenes this involves an array and an extra iteration over that, but that's generally very cheap, and the advantage of this method over the other answers to this question is that it works on plain enumerables and does not involve tricky mutable state issues that are hard to read and easy to get wrong.

By contrast, iterating in reverse, while not rocket science, is much more prone to off-by-one errors and harder to read; and it also relies on internals of the collection such as not changing order in between deletions (e.g. better not be a binary heap, say). Manually adding items that should be deleted to a temporary list is just unnecessary code - that's what .ToArray() will do just fine :-).

Eamon Nerbonne
+2  A: 

I need to remove an item in that enumerator

As long as this is a single item that's not a problem. The rule is that you cannot continue to iterate after modifying the collection. Thus:

foreach (var item in collection) {
    if (item.Equals(toRemove) {
        collection.Remove(toRemove);
        break;      // <== stop iterating!!
    }
}
Hans Passant
A: 

an enumerator always has a private field pointing to the real collection.
you can get it via reflection.modify it.
have fun.

Behrooz