Sean Anderson
Sean Anderson

Reputation: 29301

Remove N items from IList where match predicate

I would like to remove N items from an IList collection. Here's what I've got:

public void RemoveSubcomponentsByTemplate(int templateID, int countToRemove)
{
    //  TaskDeviceSubcomponents is an IList
    var subcomponents = TaskDeviceSubcomponents.Where(tds => tds.TemplateID == templateID).ToList();

    if (subcomponents.Count < countToRemove)
    {
        string message = string.Format("Attempted to remove more subcomponents than found. Found: {0}, attempted: {1}", subcomponents.Count, countToRemove);
        throw new ApplicationException(message);
    }

    subcomponents.RemoveRange(0, countToRemove);
}

Unfortunately, this code does not work as advertised. TaskDeviceSubcomponents is an IList, so it doesn't have the RemoveRange method. So, I call .ToList() to instantiate an actual List, but this gives me a duplicate collection with references to the same collection items. This is no good because calling RemoveRange on subcomponents does not affect TaskDeviceSubcomponents.

Is there a simple way to achieve this? I'm just not seeing it.

Upvotes: 2

Views: 892

Answers (1)

davisoa
davisoa

Reputation: 5439

Unfortunately, I think you need to remove each item individually. I would change your code to this:

public void RemoveSubcomponentsByTemplate(int templateID, int countToRemove)
{
    //  TaskDeviceSubcomponents is an IList
    var subcomponents = TaskDeviceSubcomponents
                         .Where(tds => tds.TemplateID == templateID)
                         .Take(countToRemove)
                         .ToList();

    foreach (var item in subcomponents)
    {
        TaskDeviceSubcomponents.Remove(item);
    }
}

Note that it is important to use ToList here so you are not iterating TaskDeviceSubcomponents while removing some of its items. This is because LINQ uses lazy evaluation, so it doesn't iterate over TaskDeviceSubcomponents until you iterate over subcomponents.

Edit: I neglected to only remove the number of items contained in countToRemove, so I added a Take call after the Where.

Edit 2: Specification for the Take()-Method: http://msdn.microsoft.com/en-us/library/bb503062(v=vs.110).aspx

Upvotes: 3

Related Questions