2
myGenericList.RemoveAll(x => (x.StudentName == "bad student"));

Works great, but a bindinglist does not have this method. How can I create an extension method for the bindinglist that takes as input a predicate and does the magic like the canned removeall for List

thankyou

Gullu
  • 3,477
  • 7
  • 43
  • 70
  • The extension method is going to do the same thing as if you did it manually. – svick Jul 06 '11 at 15:22
  • Damn. i was hoping for some cool lambda trick or something. Anyone ? – Gullu Jul 06 '11 at 15:24
  • It's not really a lambda trick per se, lambdas only will help you with the predicates. As for Linq, you can use Linq to help with selecting, but the Linq methods don't really mutate IEnumerable sequences. So you'd need a combo of select, then remove. – James Michael Hare Jul 06 '11 at 15:36
  • thanks svick and James. Learned many new things from your responses. – Gullu Jul 06 '11 at 16:27

2 Answers2

5

Like I said in a comment, there is no magic in extension methods, just write the code the same way as if you wrote it normally, just put it in a static method in a static class and use the this keyword:

public static void RemoveAll<T>(this BindingList<T> list, Func<T, bool> predicate)
{
    foreach (var item in list.Where(predicate).ToArray())
        list.Remove(item);
}

You have to use ToArray() (or ToList()), because Where() is lazy and only enumerates the collection when needed and you can't enumerate changing collection.

Although this solution is quite slow (O(N2)), because every Remove() has to look through the collection to find the correct item to remove. We can do better:

public static void FastRemoveAll<T>(this BindingList<T> list, Func<T, bool> predicate)
{
    for (int i = list.Count - 1; i >= 0; i--)
        if (predicate(list[i]))
            list.RemoveAt(i);
}

This uses the fact that we can get to i-th item in constant time, so the whole method is O(N). The iteration is easier to write backwards, so that indexes of items we have yet to consider aren't changing.

EDIT: Actually the second solution is still O(N2), because every RemoveAt() has to move all the items after the one that was removed.

svick
  • 236,525
  • 50
  • 385
  • 514
  • True, either way they perform in the same order of magnitude. The linq expression in each of our two solutions is probably more maintainable for a novice developer who doesn't understand what's going on. – James Michael Hare Jul 06 '11 at 15:50
1

I'd say:

public static class BindingListExtensions
{
    public static void RemoveAll<T>(this BindingList<T> list, Func<T, bool> predicate)
    {
        // first check predicates -- uses System.Linq
        // could collapse into the foreach, but still must use 
        // ToList() or ToArray() to avoid deferred execution                       
        var toRemove = list.Where(predicate).ToList();

        // then loop and remove after
        foreach (var item in toRemove)
        {
            list.Remove(item);
        }
    }
}

And for those interested in the minutia, seems ToList() and ToArray() are so close to the same performance (and in fact each can be faster based on the circumstance) as to be negligible: I need to iterate and count. What is fastest or preferred: ToArray() or ToList()?

Community
  • 1
  • 1
James Michael Hare
  • 37,767
  • 9
  • 73
  • 83
  • That first `foreach` could be easily avoided using `ToArray()`. – svick Jul 06 '11 at 15:34
  • True, I thought of that right as I entered it and then updated it. Used ToList() instead, though. – James Michael Hare Jul 06 '11 at 15:34
  • yeah, but I think `ToArray()` is slightly clearer here, because you don't mutate the result and array is less mutable than list. – svick Jul 06 '11 at 15:42
  • In general I agree, but confined within the method it's fairly safe either way since we're only using it as a temporary list, if I were returning the results I'd be much more concerned, of course. – James Michael Hare Jul 06 '11 at 15:52
  • thanks for your response. Upvoted since your solution is the easiest to understand. – Gullu Jul 06 '11 at 16:37