tags:

views:

93

answers:

3

Related to this: http://stackoverflow.com/questions/3741756/adding-new-items-dynamically-to-iqueryable-hard-coded-fake-repository/3741768#3741768

How could I build the method for the following class, which would remove items from the list based on the value of one of its fields?

public class FakeProductsRepository 
{
  private readonly List<Product> fakeProducts = new List<Product>
  {
      new Product { ProductID = "xxx", Description = "xxx", Price = 1000 },
      new Product { ProductID = "yyy", Description = "xxx", Price = 2000 },
      new Product { ProductID = "zzz", Description = "xxx", Price = 3000 },
  };

  public void AddProduct(string productID, string description, int price)
  {
      fakeProducts.Add(new Product
      {
          ProductID = productID,
          Description = description,
          Price = price,
      });
  }

  public void RemoveProduct(string productID)
  {
      ????????
      //How to remove the item from the fakeProducts List where ProductID == productID?
  }

  public IQueryable<Product> Products
  {
      get { return fakeProducts.AsQueryable(); }
  }
}

The problem method is pointed out with "???????" and the comment string.

+2  A: 

Try using LINQ with the where clause. Note you need .NET 3.5.

var reducedProducts = from r in fakeProducts
                      where r.ProductID != productID
                      select r;

This will give you a reduced collection.

You could also use the RemoveAll() method, which takes a predicate but modifies the current collection.

fakeProducts.RemoveAll(delegate (Product r) { return r.ProductID != productID; });
linuxuser27
Should note, that RemoveAll is a new method in .NET 4 and you don't need to remove `readonly` to use it. Readonly 'works' only on field reference itself, but not on the list contents.
Yacoder
@Yacoder Thats not true. `RemoveAll()` has been around for `List` since .NET 2.0. Check the API link above. Good call on the `readonly`. I have modified my answer.
linuxuser27
@linuxuser27: Oh, right. Then you should be able to just write fakeProducts.RemoveAll(product => product.ProductID == productID);
Yacoder
Anyway, +1 for RemoveAll, thanks!
Yacoder
+3  A: 

In general, for a collection I'd use this code:

var productsToRemove = fakeProducts.Where(p => p.ProductID == productID).ToList();
foreach(var product in productsToRemove)
{
   fakeProducts.Remove(product);
}

Don't forget the ToList(), or you can get an InvalidOperationException saying "Collection was modified".

Update (thanks to linuxuser27): But List<T> also has a special method, taking Predicate<T>:

fakeProducts.RemoveAll(product => product.ProductID == productID);
Yacoder
+1  A: 

If looking up items by product ID is a common operation, consider replacing the list with a dictionary keyed by the product ID.

If, however, you want to keep the List as the data structure, then just loop over it (this assumes that there's only one product with a given ProductID):

foreach(Product p in fakeProducts)
{
    if(p.ProductId == productID)
    {
         fakeProducts.remove(p);
         break;
    }
}
Yuliy
This will throw InvalidOperationException ("Collection was modified").
Yacoder
No, it won't. You break out right after you modify, before continuing iteration.
Yuliy
Then there's no reason to use foreach. Just call ContainsKey and if it returns true, call Remove.
Jim Mischel
@Jim: I maybe wasn't clear. I was proposing the swap from a list to a dictionary as one option (with the ContainsKey/remove implementation for removal as you noted), but for a List there's no explicit way to see which product IDs are inside it short of a traversal.
Yuliy
@Yuliy: My bad. It appears that I didn't read your response completely. I see now that your second sentence is talking about List. (although you might want to change "linked list" to "List")
Jim Mischel