3

I have a cart.Lines List and want to remove all items where quantity == 0

This is a list that holds collection of CartLine objects:

public class Cart
{
    private IList<CartLine> lines = new List<CartLine>();
    public IList<CartLine> Lines { get { return lines; } set { lines = value; } }
}   

public class CartLine
{
    Product Product {get; set;}
    int Quantity {get; set;}

}

So something like:

cart.Lines.RemoveAll(x => x.Quantity == 0)

I only get Remove and RemoveAt, not RemoveAll !

Also can't remove in a foreach loop, get error: Collection was modified; enumeration operation may not execute.

I have now managed to do it with this code, surely there must be something more efficient ?

var myList = cart.Lines.ToList();
myList.RemoveAll(x => x.Quantity == 0);
cart.Lines = myList;

Okay Problem solved!Thanks guys, this here does it:

cart.Lines = cart.Lines.Where(x => x.Quantity != 0);

Brian Roach
  • 76,169
  • 12
  • 136
  • 161
LaserBeak
  • 3,257
  • 10
  • 43
  • 73
  • It looks like a ToList()/RemoveAll() combo may even be the most efficient... (see the timings below), but BARELY. The Where() on negative condition followed by ToList() is nearly as fast. I think the thing is RemoveAll() is very optimized for List, so it's extremely fast where the other Linq methods using iterators (yield return) which tend to be a bit slower. – James Michael Hare Jul 15 '11 at 14:31

6 Answers6

7

If Lines is a List<T>, then the simplest way is to just write:

cart.Lines.RemoveAll(x => x.Quantity == 0);

If Lines is an IEnumerable<T>, though, you could select the negative (as Vlad suggested) - you could also change to a list using ToList() and then do RemoveAll() but that would be overkill.

cart.Lines = cart.Lines.Where(x => x.Quantity != 0);

UPDATE:

Since you said Lines is an IList<T>, then you will want to select the negative and convert to a list like:

cart.Lines = cart.Lines.Where(x => x.Quantity != 0).ToList();

Or you can conver to a List<T> using ToList() then call RemoveAll() and then save back:

var temp = cart.Lines.ToList();
temp.RemoveAll(x => x.Quantity != 0);
cart.Lines = temp;

Incidentally, as an FYI I timed both building a remove list and then using Remove() vs selecting the negative using Where() and calling ToList() and the Where/ToList combo was much faster which makes sense because both allocate memory, but the Where/ToList does a lot less memory shuffling.

Here's the timing for removing all the even numbers out of a list of 100,000 ints:

  • Removing all evens building a remove list and calling Remove() on each took: 3921 ms
  • Removing all evens using Where() on negative and then ToList() took: 2 ms
  • Removing all evens using ToList() on original then RemoveAll() took: 1 ms
James Michael Hare
  • 37,767
  • 9
  • 73
  • 83
3

Assuming that cart.Lines is List<>: cart.Lines.RemoveAll(x => x.Quantity == 0);

treetey
  • 829
  • 1
  • 6
  • 16
  • So use `cart.Lines = cart.Lines.Where(x => x.Quantity > 0).ToList()` – treetey Jul 15 '11 at 14:15
  • @LaserBeak - Why exactly are you using interface collection instead of the generic List collection? – Security Hound Jul 15 '11 at 14:31
  • @Ramhound, he's probably trying to decouple the implementation a bit from the interface. A lot of code analysis tools tend to recommend using IList or ICollection instead of List directly. Not saying that's necessarily good or bad, just something that comes up a lot. Yeah, he added the IList clarification in an edit. – James Michael Hare Jul 15 '11 at 14:38
1
var myResult = cart.Lines.Where(x => x.Quantity > 0)

Alternatively you can use RemoveAll

cart.Lines.RemoveAll(x => x.Quantity == 0)

Check this post that answers your question C# using LINQ to remove objects within a List

Community
  • 1
  • 1
Vlad Bezden
  • 83,883
  • 25
  • 248
  • 179
1

These queries essentially foreach over a list, and as you know, you shouldn't use them to directly modify the list. Rather you should make a list of the items to be removed using the query, and then remove them in a separate operation.

EDIT:

yeh, I forgot you can use RemoveAll to do this in one line :D

Franchesca
  • 1,453
  • 17
  • 32
1

I will go ahead and post my suggestion to this problem.

private IList<CartLine> lines = new List<CartLine>(); 

should be:

private List<CartLine> lines = new List<CartLine>(); 

This will allow you to use the suggested method of:

cart.Lines.RemoveAll(x => x.Quantity == 0);  

You do exactly that by doing it this way:

var myList = cart.Lines.ToList();       
myList.RemoveAll(x => x.Quantity == 0);              
cart.Lines = myList;   
Security Hound
  • 2,577
  • 3
  • 25
  • 42
0

You can do it as follows:

Cart cart = new Cart();
List<CartLine> cartLines = cart.Lines.ToList<CartLine>();
cartLines.RemoveAll(x => x.Quantity == 0);
cart.Lines = cartLines;

Also, you should set the CartLine Quantity and Product properties as public.

Alex Mendez
  • 5,120
  • 1
  • 25
  • 23