【问题标题】:Use LINQ to get items in one List<>, that are not in another List<>使用 LINQ 获取一个 List<> 中的项目,这些项目不在另一个 List<> 中
【发布时间】:2011-04-26 01:55:03
【问题描述】:

我假设有一个简单的 LINQ 查询可以做到这一点,我只是不确定如何。

鉴于这段代码:

class Program
{
    static void Main(string[] args)
    {
        List<Person> peopleList1 = new List<Person>();
        peopleList1.Add(new Person() { ID = 1 });
        peopleList1.Add(new Person() { ID = 2 });
        peopleList1.Add(new Person() { ID = 3 });

        List<Person> peopleList2 = new List<Person>();
        peopleList2.Add(new Person() { ID = 1 });
        peopleList2.Add(new Person() { ID = 2 });
        peopleList2.Add(new Person() { ID = 3 });
        peopleList2.Add(new Person() { ID = 4 });
        peopleList2.Add(new Person() { ID = 5 });
    }
}

class Person
{
    public int ID { get; set; }
}

我想执行一个 LINQ 查询,将peopleList2 中所有不在peopleList1 中的人提供给我。

这个例子应该给我两个人(ID = 4 & ID = 5)

【问题讨论】:

  • 也许将 ID 设置为只读是个好主意,因为对象的身份不应在其生存期内发生变化。当然,除非您的测试或 ORM 框架要求它是可变的。
  • 根据this diagram?,我们可以称之为“左(或右)排除连接”

标签: c# linq .net-3.5


【解决方案1】:

这可以使用以下 LINQ 表达式来解决:

var result = peopleList2.Where(p => !peopleList1.Any(p2 => p2.ID == p.ID));

通过 LINQ 表达这一点的另一种方式,一些开发人员认为这种方式更具可读性:

var result = peopleList2.Where(p => peopleList1.All(p2 => p2.ID != p.ID));

警告: 如 cmets 中所述,这些方法要求执行 O(n*m) 操作。这可能很好,但可能会引入性能问题,尤其是在数据集非常大的情况下。如果这不能满足您的性能要求,您可能需要评估其他选项。但是,由于所述要求是针对 LINQ 中的解决方案,因此此处不探讨这些选项。与往常一样,根据您的项目可能具有的性能要求评估任何方法。

【讨论】:

  • 你知道这是一个 O(n*m) 的解决方案,可以在 O(n+m) 时间内轻松解决吗?
  • @nikie,OP 要求使用 Linq 的解决方案。也许他正在尝试学习 Linq。如果这个问题是针对最有效的方式,我的问题就不一定是一样的了。
  • @nikie,愿意分享您的简单解决方案吗?
  • 这是等价的,我发现更容易理解: var result = peopleList2.Where(p => peopleList1.All(p2 => p2.ID != p.ID));
  • @Menol - 批评正确回答问题的人可能有点不公平。人们不需要预测未来人们可能偶然发现答案的所有方式和背景。实际上,您应该将其指向 nikie - 谁花时间表示他们知道替代方案但未提供。
【解决方案2】:

如果你覆盖 People 的相等性,那么你也可以使用:

peopleList2.Except(peopleList1)

Except 应该比Where(...Any) 变体快得多,因为它可以将第二个列表放入哈希表中。 Where(...Any) 的运行时间为 O(peopleList1.Count * peopleList2.Count),而基于 HashSet&lt;T&gt; 的变体(几乎)的运行时间为 O(peopleList1.Count + peopleList2.Count)

Except 隐式删除重复项。这应该不会影响您的案例,但可能是类似案例的问题。

或者如果你想要快速的代码但不想覆盖相等性:

var excludedIDs = new HashSet<int>(peopleList1.Select(p => p.ID));
var result = peopleList2.Where(p => !excludedIDs.Contains(p.ID));

此变体不会删除重复项。

【讨论】:

  • 只有在 Equals 被覆盖以比较 ID 时才有效。
  • 这就是为什么我写了你需要覆盖平等。但是我添加了一个即使没有它也可以工作的示例。
  • 如果 Person 是一个结构体,它也可以工作。尽管如此,Person 似乎是一个不完整的类,因为它有一个名为“ID”的属性不能识别它 - 如果它确实识别了它,那么 equals 将被覆盖,因此相等的 ID 意味着相等的 Person。一旦修复了 Person 中的错误,这种方法就更好了(除非通过将“ID”重命名为不会因看起来是标识符而误导的其他内容来修复错误)。
  • 如果您谈论的是字符串列表(或其他基本对象),它也很有效,这是我遇到此线程时正在搜索的内容。
  • @DanKorn 同样,与 where 相比,这是一个更简单的解决方案,用于基本比较,int,objects ref,strings。
【解决方案3】:

或者如果你想要它没有否定:

var result = peopleList2.Where(p => peopleList1.All(p2 => p2.ID != p.ID));

基本上它说从 peopleList2 中获取所有内容,其中 peopleList1 中的所有 id 都与 peoplesList2 中的 id 不同。

与接受的答案略有不同:)

【讨论】:

  • 这种方法(超过 50,000 个项目的列表)明显快于 ANY 方法!
  • 这可能会更快,因为它很懒。请注意,这还没有做任何真正的工作。直到您枚举列表,它才会真正完成工作(通过调用 ToList 或将其用作 foreach 循环的一部分等)
【解决方案4】:

由于迄今为止所有的解决方案都使用流利的语法,这里有一个查询表达式语法的解决方案,对于那些感兴趣的人:

var peopleDifference = 
  from person2 in peopleList2
  where !(
      from person1 in peopleList1 
      select person1.ID
    ).Contains(person2.ID)
  select person2;

我认为它与某些人感兴趣的答案有很大不同,甚至认为它很可能对于列表来说不是最佳的。现在对于带有索引 ID 的表,这肯定是要走的路。

【讨论】:

  • 谢谢。第一个与查询表达式语法有关的答案。
【解决方案5】:

聚会有点晚了,但也与 Linq to SQL 兼容的一个很好的解决方案是:

List<string> list1 = new List<string>() { "1", "2", "3" };
List<string> list2 = new List<string>() { "2", "4" };

List<string> inList1ButNotList2 = (from o in list1
                                   join p in list2 on o equals p into t
                                   from od in t.DefaultIfEmpty()
                                   where od == null
                                   select o).ToList<string>();

List<string> inList2ButNotList1 = (from o in list2
                                   join p in list1 on o equals p into t
                                   from od in t.DefaultIfEmpty()
                                   where od == null
                                   select o).ToList<string>();

List<string> inBoth = (from o in list1
                       join p in list2 on o equals p into t
                       from od in t.DefaultIfEmpty()
                       where od != null
                       select od).ToList<string>();

http://www.dotnet-tricks.com/Tutorial/linq/UXPF181012-SQL-Joins-with-C致敬

【讨论】:

    【解决方案6】:

    Klaus 的回答很好,但 ReSharper 会要求您“简化 LINQ 表达式”:

    var result = peopleList2.Where(p =&gt; peopleList1.All(p2 =&gt; p2.ID != p.ID));

    【讨论】:

    • 值得注意的是,如果有多个属性绑定两个对象(想想 SQL 复合键),则此技巧将不起作用。
    • Alrekr - 如果您的意思是“如果需要比较更多属性,您将需要比较更多属性”,那么我会说这很明显。
    【解决方案7】:

    此 Enumerable Extension 允许您定义要排除的项目列表和用于查找用于执行比较的键的函数。

    public static class EnumerableExtensions
    {
        public static IEnumerable<TSource> Exclude<TSource, TKey>(this IEnumerable<TSource> source,
        IEnumerable<TSource> exclude, Func<TSource, TKey> keySelector)
        {
           var excludedSet = new HashSet<TKey>(exclude.Select(keySelector));
           return source.Where(item => !excludedSet.Contains(keySelector(item)));
        }
    }
    

    你可以这样使用它

    list1.Exclude(list2, i => i.ID);
    

    【讨论】:

    • 拥有@BrianT 拥有的代码,我如何将其转换为使用您的代码?
    • 在 Bertrand 的回复中使用 EnumerableExtensions 代码在某处创建一个新类。在执行查询的类中添加 using 语句。然后把选择码改成var result = peopleList2.Exclude(peopleList1, i =&gt; i.ID);
    【解决方案8】:

    一旦你编写了一个通用的 FuncEqualityComparer,你就可以在任何地方使用它。

    peopleList2.Except(peopleList1, new FuncEqualityComparer<Person>((p, q) => p.ID == q.ID));
    
    public class FuncEqualityComparer<T> : IEqualityComparer<T>
    {
        private readonly Func<T, T, bool> comparer;
        private readonly Func<T, int> hash;
    
        public FuncEqualityComparer(Func<T, T, bool> comparer)
        {
            this.comparer = comparer;
            if (typeof(T).GetMethod(nameof(object.GetHashCode)).DeclaringType == typeof(object))
                hash = (_) => 0;
            else
                hash = t => t.GetHashCode(); 
        }
    
        public bool Equals(T x, T y) => comparer(x, y);
        public int GetHashCode(T obj) => hash(obj);
    }
    

    【讨论】:

      【解决方案9】:

      这是一个工作示例,可让您获得求职者不具备的 IT 技能 已经有了。

      //Get a list of skills from the Skill table
      IEnumerable<Skill> skillenum = skillrepository.Skill;
      //Get a list of skills the candidate has                   
      IEnumerable<CandSkill> candskillenum = candskillrepository.CandSkill
             .Where(p => p.Candidate_ID == Candidate_ID);             
      //Using the enum lists with LINQ filter out the skills not in the candidate skill list
      IEnumerable<Skill> skillenumresult = skillenum.Where(p => !candskillenum.Any(p2 => p2.Skill_ID == p.Skill_ID));
      //Assign the selectable list to a viewBag
      ViewBag.SelSkills = new SelectList(skillenumresult, "Skill_ID", "Skill_Name", 1);
      

      【讨论】:

        【解决方案10】:

        首先,从条件集合中提取id

        List<int> indexes_Yes = this.Contenido.Where(x => x.key == 'TEST').Select(x => x.Id).ToList();
        

        其次,使用“比较” estament 来选择与选择不同的 id

        List<int> indexes_No = this.Contenido.Where(x => !indexes_Yes.Contains(x.Id)).Select(x => x.Id).ToList();
        

        显然您可以使用 x.key != "TEST",但这只是一个示例

        【讨论】:

          猜你喜欢
          • 2023-03-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-03-07
          • 1970-01-01
          • 2023-03-26
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多