Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Filter IEnumerable

Tags:

c#

filtering

I have 3 classes with the following properties:

OfferList class:

Guid Id
IEnumerable<Offer> Offers

Offer class:

Guid Id
Product Product
bool IsSealed

Product class:

Guid Id

An OfferList contains multiple Offers and an Offer has exact 1 Product.

How can I filter the OfferList to contain only Offers which are not sealed?

OfferList offerList = this.GetOfferList(id).Offers.Where(o => !o.IsSealed));

This returns an IEnumerable of type Offer instead of filtering the OfferList.

like image 995
niklr Avatar asked Dec 04 '12 10:12

niklr


People also ask

How do I filter in C#?

C# filter list with iteration. In the first example, we use a foreach loop to filter a list. var words = new List<string> { "sky", "rock", "forest", "new", "falcon", "jewelry" }; var filtered = new List<string>(); foreach (var word in words) { if (word. Length == 3) { filtered.

What is the IEnumerable?

IEnumerable is an interface defining a single method GetEnumerator() that returns an IEnumerator interface. It is the base interface for all non-generic collections that can be enumerated. This works for read-only access to a collection that implements that IEnumerable can be used with a foreach statement.


3 Answers

Interesting - it is a little confusing what you're asking here.

I think you're asking how to have Offers filtered in-place. If so:

IEnumerable<T> is immutable (you have to cast to a concrete List<T> or similar to get mutability), and Offers is an IEnumerable<T> - so you can't expect Offers.Where(o => !o.IsSealed)); to change the Offers property - it returns an enumerable that filters the source as you enumerate it.

Instead you'd do

var offerList = this.GetOfferList(id)
offerList.Offers = offerList.Offers.Where(o => !o.IsSealed));

Note, however, that this hides the original Offers reference for any other code sharing that OfferList instance. It also doesn't actually do any filtering until you start enumerating, either. But that's often preferable. If you want it done there and then - use .ToArray() or .ToList() at the end of the Where to force the enumeration to be done.

A better approach would be to have a property or method on the OfferList class that returns a new enumerable on-demand:

public IEnumerable<Offer> UnsealedOffers {
  get {
    return Offers.Where(o => !o.IsSealed);
  }
}

That way you're not destroying the master enumerable.

Note that this code is susceptible to Offers being null (a NullReferenceException) and that it's not great to return null enumerables (return empty ones instead) - so if there is a chance that Offers can be null - then either stop that from happening; or use:

return (Offers ?? Enumerable.Empty<Offer>()).Where(o => !o.IsSealed);
like image 144
Andras Zoltan Avatar answered Sep 28 '22 02:09

Andras Zoltan


If you mean that you want to update your original OfferList instance, you could just assign the filtered sequence to its Offers field:

OfferList offerList = this.GetOfferList(id);
offerList.Offers = offerList.Offers.Where(o => !o.IsSealed);
like image 22
Douglas Avatar answered Sep 28 '22 01:09

Douglas


Do you need a new list of un-sealed offers?

List<Offer> offers = this.GetOfferList(id).Offers
                         .Where(o => !o.IsSealed)
                         .ToList();
like image 35
Tim Schmelter Avatar answered Sep 28 '22 00:09

Tim Schmelter