This method removes all elements fulfilling a condition and returns a list of the removed elements. It only iterates once.
public static List<T> RemoveAll<T>(List<T> input, Func<T,bool> condition)
{
List<T> removedEntries = new List<T>();
int offset = 0;
for(int i = 0; i < input.Count - offset; i++)
{
while(i < input.Count - offset && condition.Invoke(input[i + offset]))
{
removedEntries.Add(input[i + offset]);
offset++;
Console.WriteLine("i="+i+", offset="+offset);
}
if(i < input.Count - offset)
{
input[i] = input[i+offset];
}
}
input.RemoveRange(input.Count - offset, offset);
return removedEntries;
}
We loop over the list and check if an element matches the condition. If the condition is matched, the element after that element is copied into the position. So all elements that don't fulfill the condition will be at the start of the list, and all elements that fulfill the condition are at the end of the list. In the last step, the elements at the end of the list are removed.
It might be wise to give an initial capacity to the removedEntries
list. Per default, a list has a capacity of 4, which is doubled every time it is exceeded. If you have 100 elements to be removed, the capacity has to be extended 5 times. This is an O(n)
operation each time. If you can estimate that you will delete about 10% of the elements, you might write
List<T> removedEntries = new List<T>(input.Count / 10);
This might save you some time, but on the other hand, if you don't need the full initial capacity of the list, you waste some memory.
Online demo: https://dotnetfiddle.net/dlthkH