5

I have a generic list that I'm removing items out of using List.Remove(Object). I have been removing items but whenever I get to the fifth item I'm removing it fails and does not remove it from the list. It doesn't seem to matter what I'm removing but everytime I try to remove five items it fails on the fifth item.

What could be causing this? Looking at the documentation for List(Of T).Remove, it doesn't specify what algorithm they're using to remove the item.

KyleMit
  • 30,350
  • 66
  • 462
  • 664
Cody C
  • 3,087
  • 4
  • 24
  • 32
  • Could you present us some code ? the type of the object in list, the size, etc – jose Dec 17 '09 at 16:39
  • 1
    What specific types are you adding to the List? Are they builtin .NET types, or are the Objects of a custom type you created? – Justin Niessner Dec 17 '09 at 16:39
  • They're custom objects I've created. I'm removing them from the list by passing the object I want to remove from the .remove method. I'm not doing this in a loop. – Cody C Dec 17 '09 at 16:46
  • I should have been more clear when I said fails. It just doesn't remove it from the list. No exceptions. – Cody C Dec 17 '09 at 16:46
  • Then it should work without any problem... we'd need a code example or more details to help you. – Meta-Knight Dec 17 '09 at 17:19
  • @Cody: Check what `Remove` returns in this case. The method returns false if the item was not found or if it was otherwise not successfully removed; it doesn't throw an exception. – Dan Tao Dec 17 '09 at 18:52
  • @Cody: We really *really* need to see some code here. The cause is obviously quite subtle and seeing lines of code will definitely help. I strongly suspect Kyralessa's answer is correct, but cannot be sure without seeing code. – Christian Hayter Dec 18 '09 at 13:06
  • Are you trying to remove an item while doing a FOR EACH loop? If so, you should consider making your FOR EACH ... IN [list].ToArray() and then removing from there. – SQLMason Mar 03 '14 at 15:12

5 Answers5

13

Remove is going to match based on calling .Equals on your objects. By default for a given object it'll only match the same object. If you want two objects with the same properties to be considered equal even if they're not the same object, you need to override the Equals method and put your logic there.

However, another good option is to use RemoveAll and pass in an anonymous delegate or a lambda expression with the criteria you're looking for. E.g.:

customers.RemoveAll(customer => customer.LastName.Equals(myCustomer.LastName));

Of course that only works if you really want to remove all the matching items, and/or if you're certain there will only be one that matches.

Ryan Lundy
  • 204,559
  • 37
  • 180
  • 211
2

If you are using an indexed based method to remove items from the list and remember that the indexes of items after the one you remove will change by -1 as you remove the ones before it.

Jeffrey Hines
  • 1,766
  • 1
  • 13
  • 16
  • 1
    +1 - for what I'd wager is the correct answer despite the scant details in the question. – Joe Dec 17 '09 at 18:33
  • Which is why, if he's using an index-based method, he should be enumerating from the end of the list to the beginning. – Dan Tao Dec 17 '09 at 18:49
  • Or on each step he can *either* increment the loop variable *or* remove an item, but not both. – Ryan Lundy Dec 17 '09 at 18:59
0

How many items are in your list? How are you removing them, just looping through? Keep in mind these are 0-based lists. If you're doing any sort of For loop with an integer it may not work as you're removing items.

Shawn Steward
  • 6,773
  • 3
  • 24
  • 45
0

As a general rule, you should not modify a collection that your are looping over, only the items inside of that collection. The problem with removing items inside of a for each loop is that it changes the collection that is being looped and interferes with the list count and iteration location.

Common solutions are to:

  1. Loop through the collection backwards so interferences with the iterator won't impact the execution
  2. Create a new collection so you can modify one collection and loop over another.

Loop Backwards:

Here's an extension method that takes in a Collection(Of T) (you could use a List(Of T), but the list class already exposes a RemoveAll method which basically does this same thing).
We'll loop backwards and check the validity of each item in the collection based on the passed in lambda function. If it matches, then we'll remove it.

<Extension()>
Public Sub RemoveEach(Of T)(ByRef col As Collection(Of T),
                            ByVal match As Func(Of T, Boolean))
    For i = col.Count - 1 To 0 Step -1
        If match(col(i)) Then col.RemoveAt(i)
    Next
End Sub

Then use like this:

Dim col = New Collection(Of Integer)({1, 2, 3, 4}.ToList)
col.RemoveEach(Function(i) (i Mod 2) = 0)
'Produces list of 1 & 3

Create New Collection:

It is more efficient to keep track of the collection index and remove items using RemoveAt instead of Remove which takes an object and then checks the rest of the collection for matches against that object. However, if you really wanted a way to process removing items within a For Loop, you could call ToList on the original collection, thereby creating a new list. Then, you can loop over the new list to find items that need to be removed from the original. Whenever you find an object, you can safely remove it from the original collection, because it is not currently being enumerated over.

<Extension()>
Public Sub RemoveEachObject(Of T)(ByRef col As Collection(Of T), 
                                  ByVal match As Func(Of T, Boolean))
    For Each o As T In col.ToList()
        If match(o) Then col.Remove(o)
    Next
End Sub

For more info, check out this great answer to Remove from a List within a 'foreach' loop.

Community
  • 1
  • 1
KyleMit
  • 30,350
  • 66
  • 462
  • 664
-1

If you are using for loop to remove elements, you should consider using the foreach, it is more suitable for collections, lists and numerable objects

jose
  • 2,733
  • 4
  • 37
  • 51
  • 3
    If he removes an element from the list in the middle of a foreach loop he'll get an `InvalidOperationException` as the collection was modified. – Dan Tao Dec 17 '09 at 18:48