Best way to remove items from a collection

前端 未结 15 2021
天命终不由人
天命终不由人 2020-12-08 05:47

What is the best way to approach removing items from a collection in C#, once the item is known, but not it\'s index. This is one way to do it, but it seems inelegant at be

相关标签:
15条回答
  • 2020-12-08 06:43

    If RoleAssignments is a List<T> you can use the following code.

    workSpace.RoleAssignments.RemoveAll(x =>x.Member.Name == shortName);
    
    0 讨论(0)
  • 2020-12-08 06:44

    The best way to do it is by using linq.

    Example class:

     public class Product
        {
            public string Name { get; set; }
            public string Price { get; set; }      
        }
    

    Linq query:

    var subCollection = collection1.RemoveAll(w => collection2.Any(q => q.Name == w.Name));
    

    This query will remove all elements from collection1 if Name match any element Name from collection2

    Remember to use: using System.Linq;

    0 讨论(0)
  • 2020-12-08 06:45

    @smaclell asked why reverse iteration was more efficient in in a comment to @sambo99.

    Sometimes it's more efficient. Consider you have a list of people, and you want to remove or filter all customers with a credit rating < 1000;

    We have the following data

    "Bob" 999
    "Mary" 999
    "Ted" 1000
    

    If we were to iterate forward, we'd soon get into trouble

    for( int idx = 0; idx < list.Count ; idx++ )
    {
        if( list[idx].Rating < 1000 )
        {
            list.RemoveAt(idx); // whoops!
        }
    }
    

    At idx = 0 we remove Bob, which then shifts all remaining elements left. The next time through the loop idx = 1, but list[1] is now Ted instead of Mary. We end up skipping Mary by mistake. We could use a while loop, and we could introduce more variables.

    Or, we just reverse iterate:

    for (int idx = list.Count-1; idx >= 0; idx--)
    {
        if (list[idx].Rating < 1000)
        {
            list.RemoveAt(idx);
        }
    }
    

    All the indexes to the left of the removed item stay the same, so you don't skip any items.

    The same principle applies if you're given a list of indexes to remove from an array. In order to keep things straight you need to sort the list and then remove the items from highest index to lowest.

    Now you can just use Linq and declare what you're doing in a straightforward manner.

    list.RemoveAll(o => o.Rating < 1000);
    

    For this case of removing a single item, it's no more efficient iterating forwards or backwards. You could also use Linq for this.

    int removeIndex = list.FindIndex(o => o.Name == "Ted");
    if( removeIndex != -1 )
    {
        list.RemoveAt(removeIndex);
    }
    
    0 讨论(0)
  • 2020-12-08 06:45

    What type is the collection? If it's List, you can use the helpful "RemoveAll":

    int cnt = workspace.RoleAssignments
                          .RemoveAll(spa => spa.Member.Name == shortName)
    

    (This works in .NET 2.0. Of course, if you don't have the newer compiler, you'll have to use "delegate (SPRoleAssignment spa) { return spa.Member.Name == shortName; }" instead of the nice lambda syntax.)

    Another approach if it's not a List, but still an ICollection:

       var toRemove = workspace.RoleAssignments
                                  .FirstOrDefault(spa => spa.Member.Name == shortName)
       if (toRemove != null) workspace.RoleAssignments.Remove(toRemove);
    

    This requires the Enumerable extension methods. (You can copy the Mono ones in, if you are stuck on .NET 2.0). If it's some custom collection that cannot take an item, but MUST take an index, some of the other Enumerable methods, such as Select, pass in the integer index for you.

    0 讨论(0)
  • 2020-12-08 06:46

    If it's an ICollection then you won't have a RemoveAll method. Here's an extension method that will do it:

        public static void RemoveAll<T>(this ICollection<T> source, 
                                        Func<T, bool> predicate)
        {
            if (source == null)
                throw new ArgumentNullException("source", "source is null.");
    
            if (predicate == null)
                throw new ArgumentNullException("predicate", "predicate is null.");
    
            source.Where(predicate).ToList().ForEach(e => source.Remove(e));
        }
    

    Based on: http://phejndorf.wordpress.com/2011/03/09/a-removeall-extension-for-the-collection-class/

    0 讨论(0)
  • 2020-12-08 06:48

    This is my generic solution

    public static IEnumerable<T> Remove<T>(this IEnumerable<T> items, Func<T, bool> match)
        {
            var list = items.ToList();
            for (int idx = 0; idx < list.Count(); idx++)
            {
                if (match(list[idx]))
                {
                    list.RemoveAt(idx);
                    idx--; // the list is 1 item shorter
                }
            }
            return list.AsEnumerable();
        }
    

    It would look much simpler if extension methods support passing by reference ! usage:

    var result = string[]{"mike", "john", "ali"}
    result = result.Remove(x => x.Username == "mike").ToArray();
    Assert.IsTrue(result.Length == 2);
    

    EDIT: ensured that the list looping remains valid even when deleting items by decrementing the index (idx).

    0 讨论(0)
提交回复
热议问题