B.K.
B.K.

Reputation: 10152

C# : Removing an item from a List<T> without knowing its index?

I'm using FileSystemWatcher class to monitor a folder and update the list as events arise. I'm using the following class to hold information for each file:

public class FileItem
{
    public string Name { get; set; }
    public string Path { get; set; }
}

The following list holds the collection of that information:

public static List<FileItem> folder = new List<FileItem>();

I added some FileItem objects into the list. However, to remove a specific item that has a matching name, I couldn't just use foreach() loop because enumerations change and as soon as the file is removed, I get an exception. So, I added a break (since there will only be one file with the same name) to break out of the foreach() loop after an item was removed... but I'm not sure if it's the most efficient way to do it. Is there a simpler and more appropriate way? Here's my code for the removal:

private static void OnChanged(object source, FileSystemArgs e)
{
    if (e.ChangeType == WatcherChangeTypes.Deleted)
    {
        foreach (var item in folder)
        {
            if (item.Name == e.Name)
            {
                folder.Remove(item);
                folder.TrimExcess();
                break;
            }
        }
    }
}

Upvotes: 2

Views: 2752

Answers (4)

Basem Sayej
Basem Sayej

Reputation: 3403

you can remove any item by populating it through linq and then removing it from the list

List<FileItem> folder = new List<FileItem>();
folder.Remove(folder.SingleOrDefault(x=>x.Name == "myname"));
folder.Remove(folder.SingleOrDefault(x => x.Path == "mypath"));

Upvotes: 7

Nikhil Agrawal
Nikhil Agrawal

Reputation: 48568

How about

private static void OnChanged(object source, FileSystemArgs e)
{
    if (e.ChangeType == WatcherChangeTypes.Deleted)
    {
        FileItem item = folder.SingleOrDefault(x => x.Name == e.Name);

        if (item != null)
            folder.Remove(item);
    }
}

Upvotes: 2

Cory Nelson
Cory Nelson

Reputation: 29981

If you can restructure your data, here is a Dictionary<> based approach:

// let the key be Name, and value be Path.
public static Dictionary<string, string> folder = new Dictionary<string, string>();

private static void OnChanged(object source, FileSystemArgs e)
{
    if (e.ChangeType == WatcherChangeTypes.Deleted)
    {
        folder.Remove(e.Name);
    }
}

This will be much more performant as the size of folder increases, because Dictionary<> operations are amortized O(1) while List<>.Remove is O(n).

This also explicitly enforces your contract of "there will only be one file with the same name", as that is a basic property of Dictionary<>.

You might want to pass in StringComparer.InvariantIgnoreCase to the dictionary's constructor, as file names are case-insensitive on Windows. This is platform-dependent -- Linux is case-sensitive -- so if you're making a shared library that needs to be robust and cross-platform, you'll want to figure out a way to select the correct comparer. This issue affects all answers, not just mine.

Upvotes: 1

ismellike
ismellike

Reputation: 629

You can try using a for loop

for(int x = folder.Count() - 1; x>=0; x--)
{
  if (folder[x].Name == e.Name)
    {
      folder.Remove(folder[x]);
    }
}

Upvotes: 1

Related Questions