【问题标题】:filtering a list using LINQ使用 LINQ 过滤列表
【发布时间】:2011-07-02 17:05:05
【问题描述】:

我有一个项目对象列表:

IEnumerable<Project> projects

一个 Project 类作为一个名为 Tags 的属性。这是一个 int[]

我有一个名为 filteredTags 的变量,它也是一个 int[]

假设我的过滤标签变量如下所示:

 int[] filteredTags = new int[]{1, 3};

我想过滤我的列表 (projects) 以仅返回具有过滤器中列出的所有标签的项目(在这种情况下,标签中至少有标签 1 和标签 3 属性)。

我试图使用 Where() 和 Contains() 但这似乎只有在我与单个值进行比较时才有效。我将如何将一个列表与另一个列表进行比较,在该列表中我需要匹配过滤列表中的所有项目??

【问题讨论】:

    标签: c# linq collections tags ienumerable


    【解决方案1】:

    编辑:更好的是,这样做:

    var filteredProjects = 
        projects.Where(p => filteredTags.All(tag => p.Tags.Contains(tag)));
    

    EDIT2:老实说,我不知道哪个更好,所以如果性能不重要,请选择您认为更具可读性的那个。如果是,您必须以某种方式对其进行基准测试。


    可能Intersect 是要走的路:

    void Main()
    {
        var projects = new List<Project>();
        projects.Add(new Project { Name = "Project1", Tags = new int[] { 2, 5, 3, 1 } });
        projects.Add(new Project { Name = "Project2", Tags = new int[] { 1, 4, 7 } });
        projects.Add(new Project { Name = "Project3", Tags = new int[] { 1, 7, 12, 3 } });
    
        var filteredTags = new int []{ 1, 3 };
        var filteredProjects = projects.Where(p => p.Tags.Intersect(filteredTags).Count() == filteredTags.Length);  
    }
    
    
    class Project {
        public string Name;
        public int[] Tags;
    }
    

    虽然一开始看起来有点难看。如果您不确定它们在列表中是否都是唯一的,您可以先将Distinct 应用到filteredTags,否则计数比较将无法按预期进行。

    【讨论】:

    • 我认为您的 Intersect 方法比您的“更好”方法更清晰
    • @AakashM:我真的不知道,我现在正在努力决定。我不喜欢Count(),因为它必须评估标签IEnumerable,但我自己也很困惑
    【解决方案2】:
    var result = projects.Where(p => filtedTags.All(t => p.Tags.Contains(t)));
    

    【讨论】:

    • 为什么?它不允许带有标签“1、2、3”的项目,还是我遗漏了什么?
    • 我不确定 ALL 是否正确,因为如果它有 ATLEAST 1 和 3 则它需要工作,但它可以有更多。 . .不是 All() 会根据 1 和 3 验证列表中的每个项目吗??
    • 我们可以稍微修改一下上面的代码,达到想要的效果。 var result = projects.Where(p => filtedTags.All(t => p.Tags.Contains(t))
    • @ooo @nyinyithann:对不起,我的英语很差,所以我误解了 OP 的问题。
    【解决方案3】:

    我们应该让项目包含(至少)所有过滤标签,或者以不同的方式说,排除那些不包含所有过滤标签的项目。 所以我们可以使用 Linq Except 来获取那些不包含的标签。然后我们可以使用Count() == 0 来只拥有那些不排除任何标签的人:

    var res = projects.Where(p => filteredTags.Except(p.Tags).Count() == 0);
    

    或者我们可以通过将Count() == 0 替换为!Any() 来稍微加快速度:

    var res = projects.Where(p => !filteredTags.Except(p.Tags).Any());
    

    【讨论】:

      【解决方案4】:
      var filtered = projects;
      foreach (var tag in filteredTags) {
        filtered = filtered.Where(p => p.Tags.Contains(tag))
      }
      

      这种方法的好处是您可以逐步优化搜索结果。

      【讨论】:

      • 这种方法的坏处是您将循环所有项目多次(与您过滤的标签一样多次)。虽然上述方法只循环项目一次。虽然在大多数情况下它们的性能相同,但在某些常见情况下(想想使用带有 yield 或 IQueryable 的 IEnumerable 的项目)它们的性能会明显更好。
      【解决方案5】:

      基于http://code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b

      EqualAll 是最能满足您需求的方法。

      public void Linq96() 
      { 
          var wordsA = new string[] { "cherry", "apple", "blueberry" }; 
          var wordsB = new string[] { "cherry", "apple", "blueberry" }; 
      
          bool match = wordsA.SequenceEqual(wordsB); 
      
          Console.WriteLine("The sequences match: {0}", match); 
      } 
      

      【讨论】:

      • -1 我认为您的回答错误且令人困惑(由于假设人们会转到链接并阅读示例并了解 EqualAll 是他们在页面中为对 SequenceEqual 方法进行示例而给出的名称) .而 SequenceEqual 不适用于这种情况,因为“项目”的标签可能比过滤的标签多,但仍应被视为成功匹配。
      猜你喜欢
      • 1970-01-01
      • 2016-12-09
      • 1970-01-01
      • 2014-04-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多