Intelligent Way of Removing Items from a List<T> While Enumerating in C#

How to remove elements from a generic list while iterating over it?

Iterate your list in reverse with a for loop:

for (int i = safePendingList.Count - 1; i >= 0; i--)
{
// some code
// safePendingList.RemoveAt(i);
}

Example:

var list = new List<int>(Enumerable.Range(1, 10));
for (int i = list.Count - 1; i >= 0; i--)
{
if (list[i] > 5)
list.RemoveAt(i);
}
list.ForEach(i => Console.WriteLine(i));

Alternately, you can use the RemoveAll method with a predicate to test against:

safePendingList.RemoveAll(item => item.Value == someValue);

Here's a simplified example to demonstrate:

var list = new List<int>(Enumerable.Range(1, 10));
Console.WriteLine("Before:");
list.ForEach(i => Console.WriteLine(i));
list.RemoveAll(i => i > 5);
Console.WriteLine("After:");
list.ForEach(i => Console.WriteLine(i));

Remove an item in a list while it's in a foreach loop c#

Try just creating another temporary list for the items that need to be deleted then when your done looping you can just delete the ones in the temp list.

List<Type> temp = new List<Type>()
foreach(item in mainList)
{
if (item.Delete)
{
temp.Add(item);
}
}

foreach (var item in temp)
{
mainList.Remove(item);
}

Can't remove items from List with remove()

You can't Remove items from the list you are iterating. It messes with the enumerator to delete things out from under it. You need to copy the items you want to keep to another list instead, and then copy it back if necessary; or create a list of items you want to remove, and remove them all at once at the end.

This has a discussion of various methods: Intelligent way of removing items from a List<T> while enumerating in C#

Remove/Add items to/from a list while iterating it

Removing multiple elements from a list 1 by 1 is a C# anti-pattern due to how lists are implemented.

Of course, it can be done with a for loop (instead of foreach). Or it can be done by making a copy of the list. But here is why it should not be done. On a list of 100000 random integers, this takes 2500 ms on my machine:

       foreach (var x in listA.ToList())
if (x % 2 == 0)
listA.Remove(x);

and this takes 1250 ms:

        for (int i = 0; i < listA.Count; i++)
if (listA[i] % 2 == 0)
listA.RemoveAt(i--);

while these two take 5 and 2 ms respectively:

        listB = listB.Where(x => x % 2 != 0).ToList();

listB.RemoveAll(x => x % 2 == 0);

This is because when you remove an element from a list, you are actually deleting from an array, and this is O(N) time, as you need to shift each element after the deleted element one position to the left. On average, this will be N/2 elements.

Remove(element) also needs to find the element before removing it. So Remove(element) will actually always take N steps - elementindex steps to find the element, N - elementindex steps to remove it - in total, N steps.

RemoveAt(index) doesn't have to find the element, but it still has to shift the underlying array, so on average, a RemoveAt is N/2 steps.

The end result is O(N^2) complexity either way, as you're removing up to N elements.

Instead, you should use Linq, which will modify the entire list in O(N) time, or roll your own, but you should not use Remove (or RemoveAt) in a loop.

How to modify or delete items from an enumerable collection while iterating through it in C#

You can remove elements from a collection if you use a simple for loop.

Take a look at this example:

        var l = new List<int>();

l.Add(0);
l.Add(1);
l.Add(2);
l.Add(3);
l.Add(4);
l.Add(5);
l.Add(6);

for (int i = 0; i < l.Count; i++)
{
if (l[i] % 2 == 0)
{
l.RemoveAt(i);
i--;
}
}

foreach (var i in l)
{
Console.WriteLine(i);
}

Best way to enumerate collection when elements may be deleted during enumeration

Do an indexed for loop, but do it backwards, because when you delete members the indexes upstream get changed.

e.g, for your string collection example:

for (int i = collection.Count() - 1; i >= 0; i--)
{
// do some other stuff, then ...

if (collection[i] == "failed")
collection.RemoveAt(i);
}

NB: if you use a forward for-loop, it may appear to work at first, but it will skip an item after each remove (because of the way indexes get renumbered after a remove). So a common symptom of that bug is that removes appear to work until you get two adjacent ones. That can be quite a confusing bug. Not that its ever happened to me *cough*

Using C# 4.8: Most efficient way to remove items matching string patterns from a string array or string list

The best solution would be the File globbing as @jwdonahue mentioned.

https://learn.microsoft.com/en-us/dotnet/core/extensions/file-globbing

But if you want to build your own solution for that, this might be a start.

public static IEnumerable<string> Filter(string pattern, IEnumerable<string> list) {
return list.Where(e =>!Match(pattern, e));
}

public static bool Match(string pattern, string input) {
var modifiedPattern = pattern
.Replace(@"\", @"\\")
.Replace(".", @"\.")
.Replace("*", ".*")
.Replace("?", ".");
modifiedPattern = "^" + modifiedPattern + "$";
return Regex.IsMatch(input, modifiedPattern, RegexOptions.IgnoreCase);
}

Which you can call like

    var filterPattern = "?MyFileName*";
var listRemaining = Filter(filterPattern, listAll).ToList();


Related Topics



Leave a reply



Submit