使用LINQ获取一个List <>中的项目,这些项目不在另一个List <>中

JSp*_*ang 485 c# linq .net-3.5

我会假设有一个简单的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; }
}
Run Code Online (Sandbox Code Playgroud)

我想执行一个LINQ查询给我所有peopleList2不在peopleList1这个例子中的人应该给我两个人(ID = 4&ID = 5)

Kla*_*sen 838

var result = peopleList2.Where(p => !peopleList1.Any(p2 => p2.ID == p.ID));
Run Code Online (Sandbox Code Playgroud)

  • @nikie,关心分享您的简单解决方案? (43认同)
  • 你知道这是一个O(n*m)解决问题的解决方案,可以在O(n + m)时间内轻松解决吗? (33认同)
  • @nikie,OP要求使用Linq的解决方案.也许他正在努力学习Linq.如果问题是最有效的方式,我的问题不一定是一样的. (28认同)
  • @Menol - 批评正确回答问题的人可能有点不公平.人们不应该预测未来人们可能会遇到答案的所有方式和背景.实际上,你应该把它指向nikie--他花时间说明他们知道一个替代方案而不提供它. (25认同)
  • 这是等价的,我觉得更容易理解:var result = peopleList2.Where(p => peopleList1.All(p2 => p2.ID!= p.ID)); (17认同)
  • @Niki,那么什么是 O(m+n) 方法@_@? (4认同)
  • @KlausByskovPedersen 虽然您的答案是针对给定问题的,但请注意,Google 带来了像我这样在没有“使用 Linq”位的情况下搜索相同问题的人。我已经知道在哪里,但正在寻找是否有更好的解决方案。那么您会考虑在您的答案中添加“除外”位吗? (2认同)
  • 如果实际上您使用 Resharper,它会建议您将“任何”更改为“全部”,如上所述 (2认同)
  • @ChrisRogers我同意你的观点。明确地说,我并不是要批评克劳斯,我只是向他指出我所希望的问题,希望他能改善自己的下水道。如果我没有做好正确表达的话,我深表歉意。 (2认同)
  • @Gen.L他可能指的是在对象上实现IEquatable,它使用哈希表,然后使用LINQ的.Except。这并不总是可能或不切实际的。如果要比较的列表总是很小,不妨使用 .Where/.All 而不是 .Except。如果使用 .Where/.All 迭代列表需要 0.2 秒,使用 .Except 需要 0.07 秒,那么我真的要花 10 分钟实现 IEquatable、使代码库复杂化并向我团队中的其他开发人员解释 IEquatable不熟悉吗?可能不会。 (2认同)

Cod*_*aos 366

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

peopleList2.Except(peopleList1)
Run Code Online (Sandbox Code Playgroud)

Except应该比Where(...Any)变体快得多,因为它可以将第二个列表放入哈希表中.Where(...Any)具有运行时,O(peopleList1.Count * peopleList2.Count)而基于HashSet<T>(几乎)的变体具有运行时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));
Run Code Online (Sandbox Code Playgroud)

此变体不会删除重复项.

  • 这就是为什么我写道你需要覆盖相等.但我添加了一个即使没有这个也可以工作的例子. (29认同)
  • 如果Person是一个结构,它也会起作用.尽管如此,Person似乎是一个不完整的类,因为它有一个名为"ID"的属性,它不能识别它 - 如果它确定了它,那么equals将被覆盖,以便相等的ID意味着相等的Person.一旦Person中的bug被修复,这种方法就更好了(除非通过将"ID"重命名为其他不会被看似是标识符的误导来修复错误). (4认同)
  • 如果你正在谈论字符串(或其他基础对象)的列表,它也很有用,这是我在遇到这个帖子时所搜索的内容. (2认同)

小智 72

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

var result = peopleList2.Where(p => peopleList1.All(p2 => p2.ID != p.ID));
Run Code Online (Sandbox Code Playgroud)

基本上它说来自peopleList2的所有内容,其中peopleList1中的所有ID都与peoplesList2中的id不同.

从接受的答案中稍微有点不同的方法:)

  • 这种方法(超过50,000项的列表)明显快于ANY方法! (5认同)
  • 这可能更快,因为它是懒惰的.请注意,这还没有做任何实际工作.直到你枚举它实际完成工作的列表(通过调用ToList或将它作为foreach循环的一部分使用等) (5认同)

Mic*_*eyn 30

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

var peopleDifference = 
  from person2 in peopleList2
  where !(
      from person1 in peopleList1 
      select person1.ID
    ).Contains(person2.ID)
  select person2;
Run Code Online (Sandbox Code Playgroud)

我认为它与某些人感兴趣的答案有所不同,甚至认为它很可能对列表不是最理想的.现在对于具有索引ID的表,这肯定是要走的路.

  • 谢谢。第一个困扰查询表达式语法的答案。 (2认同)

Ric*_*rby 15

派对迟到了但是Linq与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>();
Run Code Online (Sandbox Code Playgroud)

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


Bri*_*n T 12

克劳斯的答案很棒,但ReSharper会要求你"简化LINQ表达":

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


Ber*_*and 8

此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)));
    }
}
Run Code Online (Sandbox Code Playgroud)

你可以这样使用它

list1.Exclude(list2, i => i.ID);
Run Code Online (Sandbox Code Playgroud)