LINQ: RemoveAll and get elements removed
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));
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+1
th element becomes the i
th element after the removal, and when you increment i
you're skipping over it.
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]);
I was looking for the same thing. I wanted to do some error logging on the items that I was removing. Since I was adding a whole bunch of validation rules, the remove-and-log call should be as concise as possible.
I've made the following extension methods:
public static class ListExtensions
{
/// <summary>
/// Modifies the list by removing all items that match the predicate. Outputs the removed items.
/// </summary>
public static void RemoveWhere<T>(this List<T> input, Predicate<T> predicate, out List<T> removedItems)
{
removedItems = input.Where(item => predicate(item)).ToList();
input.RemoveAll(predicate);
}
/// <summary>
/// Modifies the list by removing all items that match the predicate. Calls the given action for each removed item.
/// </summary>
public static void RemoveWhere<T>(this List<T> input, Predicate<T> predicate, Action<T> actionOnRemovedItem)
{
RemoveWhere(input, predicate, out var removedItems);
foreach (var removedItem in removedItems) actionOnRemovedItem(removedItem);
}
}
Example usage:
items.RemoveWhere(item => item.IsWrong, removedItem =>
errorLog.AppendLine($"{removedItem} was wrong."));