Monday, December 18, 2006

deleting items from a generic list

The code for this articles applies to C# 2.0
A more or less common task on a generic list is to remove items from it, lets see what approaches we can take:

#1 The definitive wrong way to do it:

List<Person> l1 = GetList();
//*** The wrong way
foreach (Person p in l1) {
    if (p.Age > 30)
        l1.Remove(p);
}

This code will throw an InvalidOperationException: "Collection was modified; enumeration operation may not execute."

#2 Kinda works (and allows you to perform an action on the item being deleted)

List<int> ints = new List<int>();
ints.Add(1);
ints.Add(2);
ints.Add(3);
ints.Add(4);
ints.Add(5);
ints.Add(6);

ints.ForEach(delegate(int i) {
if ((i % 2) == 0) {
Console.WriteLine("removing"+i.ToString());
ints.Remove(i);
}
});



If you run that code it will work just as we expect it, but there is a problem with it, if you add all the pair numbers first you would see that it doesn't delete all of them, it doesn't throw an exception, it just breaks out of the loop and keeps going, so this method works but on very specific conditions


#3 The right way: we traverse the collection backwards and delete items

int x = ints2.Count;
while (--x>=0) {
if (ints2[x] < 4)
ints2.RemoveAt(x);
}



#4 A better way: the list has a method for this purpose

ints2.RemoveAll(delegate(int i) {
return i <4;
});



#5 But what if I need to perform an action on the item being deleted?

ints2.RemoveAll(delegate(int i) {
if (i < 4) {
//*** Perform action here
Console.WriteLine("removing :" + i.ToString());
return true;
}
else
return false;
});



if the item was a class we could call methods on it before deleting it from the list (perhaps a call to a DB)


On future posts I'll talk on detail about why exactly #1 and #2 don't work

No comments: