【问题标题】:Using LINQ, how to select conditionally some items but when no conditions select all?使用 LINQ,如何有条件地选择某些项目,但没有条件时选择全部?
【发布时间】:2012-09-06 01:06:36
【问题描述】:

我想使用 myFilters 从 myCollection 中选择元素进行过滤:

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

将返回“111”项。

但是,如果 myFilters 为空,我想从 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

将返回所有项目(“111”和“999”)。

【问题讨论】:

  • 您将无法使用单个查询来执行此操作,因为类型不同。在一种情况下,您将返回一个 IEnumerable,而在另一种情况下,您将返回一个 IEnumerable。如果您的查询以 select item.Name 而不是 select item 结尾,情况会有所不同。
  • “第一个集合”和“第二个集合”如果引用局部变量名称会更清晰。类似于:“但是,当myFilters 为空时,我想返回myCollection 中的所有项目而不进行过滤。”
  • 你是对的。现在我想清楚多了。
  • 我知道我来晚了,你能提供你的SomeClass的代码吗?我想知道它的构造函数返回什么。
  • 好吧,构造函数返回 SomeClass 的一个实例。

标签: c# linq select subset


【解决方案1】:
var result = myCollection
                   .Where(i => (!myFilters.Any() || myFilters.Contains(i.Name)));

【讨论】:

    【解决方案2】:

    试试这个:

    var result = myCollection.Where(s => !myFilters.Any() ||
                                         myFilters.Contains(s.Name));
    //EDIT: commented these lines..based on comment by @Servy
    //var result = myCollection.Where(s => myFilters.Count == 0 ||
    //                                     myFilters.Contains(s.Name));
    

    也许只计算一次过滤器集合会更好:

    bool isFilterEmpty = !myFilters.Any(); 
    //bool isFilterEmpty = myFilters.Count == 0;    //...or like this
    var result = myCollection.Where(s => isFilterEmpty || 
                                         myFilters.Contains(s.Name));
    

    编辑

    我什至会说@itsme86 的答案是正确的,但是,我猜,他混淆了你的收藏。所以他的答案应该是这样的:

    var results = myFilters.Any()
                     ? myCollection.Where(item => myFilters.Contains(item.Name))
                     : myCollection;
    

    【讨论】:

    • 您可以使用Any() 而不是Count() == 0。它不仅性能更好(它只需要让第一个项目知道是否有任何项目,而不是需要获取每个项目来获取计数),而且它在语义上也意味着你正在尝试做的事情。
    • @Servy 实际上我的答案中有Any 变体。无论如何,感谢您的澄清。
    • Any() 从 where 谓词中取出在性能方面是一个不错的举措。将Any() 留在谓词中的一个好处是,如果查询被保留并且myFiltersmyCollection 列表被更改(例如,清除、添加项目、删除项目),那么后续迭代将提供更新结果。
    【解决方案3】:

    这个怎么样?

    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 || !myFilters.Any()
    select item
    

    从两个集合中进行选择会根据您的 where 子句执行连接。上面的加入条件说加入 item.Name 等于过滤器,如果没有可用的过滤器,则选择它。

    【讨论】:

      【解决方案4】:

      如果这些集合相当大,那么我建议使用连接。它看起来像这样:

      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;
      

      使用连接的机会很容易被忽视。当列表远程很大时,这种连接方法将优于包含方法。如果它们很小且性能可以接受,则使用看起来最清晰的那个。

      【讨论】:

        【解决方案5】:

        您能做的最好的事情就是将过滤器投影到 SomeClass 中。比如:

        var results = myCollection.Any() ?
            myCollection.Where(item => myFilters.Contains(item.Name)) :
            myFilters.Select(f => new SomeClass (f));
        

        【讨论】:

        • 请注意,这不是来自myCollection 的过滤实例的子集,而是从匹配的myFilters 值创建的新SomeClass 实例的投影。
        • 当 myFilters 为空时(在 LINQPad 4 中)它不起作用。 OP 写道“如果第二个集合为空”,但表示过滤器集合。如果你没有过滤器,你应该得到整个集合;相反(使用此查询)您会得到一个空集合。
        猜你喜欢
        • 2018-12-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-22
        • 2011-01-25
        • 2012-02-17
        • 2014-07-17
        相关资源
        最近更新 更多