Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LINQ: RemoveAll and get elements removed

Tags:

c#

linq

removeall

Which is the easiest way to remove items that match some condition from a list and then, get those items.

I can think in a few ways, I don't know which is the best one:

var subList = list.Where(x => x.Condition);
list.RemoveAll(x => x.Condition);

or

var subList = list.Where(x => x.Condition);
list.RemoveAll(x => subList.Contains(x));

Is any of this one of the best ways? If it is, which one? If it's not, how should I do it?

like image 299
Diego Avatar asked Apr 16 '12 17:04

Diego


3 Answers

I would go with the first option for readability purposes, with the note that you should materialize the list first, or you'll lose the very items you're trying to select on the next line:

var sublist = list.Where(x => x.Condition).ToArray();
list.RemoveAll(x => x.Condition);

The second example is O(n^2) for no reason and the last is perfectly fine, but less readable.

Edit: now that I reread your last example, note that as it's written right now will take out every other item. You're missing the condition check and the remove line should actually be list.RemoveAt(i--); because the i+1th element becomes the ith element after the removal, and when you increment i you're skipping over it.

like image 170
Blindy Avatar answered Nov 19 '22 15:11

Blindy


I like to use a functional programming approach (only make new things, don't modify existing things). One advantage of ToLookup is that you can handle more than a two-way split of the items.

ILookup<bool, Customer> lookup = list.ToLookup(x => x.Condition);
List<Customer> sublist = lookup[true].ToList();
list = lookup[false].ToList();

Or if you need to modify the original instance...

list.Clear();
list.AddRange(lookup[false]);
like image 26
Amy B Avatar answered Nov 19 '22 13:11

Amy B


The first option is good, but it makes two runs over collection. You can do it in one run by executing the additional logic inside predicate:

        var removedItems = new List<Example>();
        list.RemoveAll(x =>
        {
            if (x.Condition)
            {
                removedItems.Add(x);
                return true;
            }

            return false;
        });

You can also wrap it into extension for convinience:

public static class ListExtensions
{
    public static int RemoveAll<T>(this List<T> list, Predicate<T> predicate, Action<T> action)
    {
        return list.RemoveAll(item =>
        {
            if (predicate(item))
            {
                action(item);
                return true;
            }

            return false;
        });
    }
}

And use like this:

        var removedItems = new List<Example>();
        list.RemoveAll(x => x.Condition, x => removedItems.Add(x));
like image 5
Eduard Grinberg Avatar answered Nov 19 '22 15:11

Eduard Grinberg