Question

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.

Was it helpful?

Solution

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.

OTHER TIPS

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.

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.

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.

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top