Using LINQ, how to select conditionally some items but when no conditions select all?

34,523

Solution 1

If these collections are going to be sizable, then I recommend using a join. It would look something like this:

var result = 
    myFilters.Any() ?
        from item in myCollection
        join filter in myFilters
        on item.Name equals filter into gj
        where gj.Any()
        select item
    : myCollection;

Opportunities for using joins are easily overlooked. This join approach will outperform the contains approach when the lists are remotely large. If they're small and performance is acceptable, then use whichever seems the clearest.

Solution 2

var result = myCollection
                   .Where(i => (!myFilters.Any() || myFilters.Contains(i.Name)));

Solution 3

The best you're going to be able to do is project the filters into SomeClass. Something like:

var results = myCollection.Any() ?
    myCollection.Where(item => myFilters.Contains(item.Name)) :
    myFilters.Select(f => new SomeClass (f));
Share:
34,523
Stécy
Author by

Stécy

Updated on July 09, 2022

Comments

  • Stécy
    Stécy almost 2 years

    I want to select elements from myCollection using myFilters for filtering:

    var myFilters = new List<string> {"111", "222"};
    var myCollection = new List<SomeClass> {
                          new SomeClass ("111"), 
                          new SomeClass ("999")
                       };
    
    from filter in myFilters
    from item in myCollection
    where item.Name == filter
    select item
    

    would return the "111" item.

    However, if myFilters is empty I want to return all the items from myCollection.

    var myFilters = new List<string> ();
    var myCollection = new List<SomeClass> {
                              new SomeClass ("111"), 
                              new SomeClass ("999")
                        };
    
    // Here's where I'm lost...
    from filter in myFilters
    from item in myCollection
    where item.Name == filter
    select item
    

    would return all items ("111" and "999").

  • Servy
    Servy over 11 years
    Rather than Count() == 0 you can use Any(). It will not only perform better (it only needs to get the first item to know if there are any vs. needing to fetch every item to get the count) but it also semantically means exactly what you're trying to do.
  • horgh
    horgh over 11 years
    @Servy Actually I've got Any variant in my answer. Anyway, thanks for clarification.
  • devgeezer
    devgeezer over 11 years
    Note that this is not a subset of filtered instances from myCollection but a projection of new SomeClass instances created from the matching myFilters values.
  • TrueWill
    TrueWill over 11 years
    It doesn't work (in LINQPad 4) when myFilters is empty. The OP wrote "if the second collection is empty" but meant the filters collection. If you have no filters you should get the entire collection; instead (with this query) you get an empty collection.
  • devgeezer
    devgeezer over 11 years
    Lifting the Any() out of the where predicate is a good move performance-wise. A nice thing about leaving the Any() in the predicate is if the query is held onto and either the myFilters or myCollection lists are altered (e.g., cleared, items added, items removed), then subsequent iteration will provide the updated result.