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

我假设有一个简单的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)

587004 次浏览

这可以使用以下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));

警告:正如评论中提到的,这些方法强制执行O(n*m)操作。这可能很好,但可能会引入性能问题,特别是如果数据集相当大。如果这不满足你的性能要求,你可能需要评估其他选项。由于所述需求是LINQ中的解决方案,然而,这些选项在这里不讨论。一如既往,根据项目可能的性能要求评估任何方法。

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

peopleList2.Except(peopleList1)

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));

此变体不删除重复项。

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

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

我认为它与给出的答案有很大的不同,有些人会感兴趣,甚至认为它很可能不适合列表。现在对于具有索引ID的表,这肯定是要走的路。

这是一个工作示例,可以获得求职者没有的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);

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

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

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

只是有点不同的方法从接受的答案:)

有点晚了,但一个很好的解决方案,这也是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>();

http://www.dotnet-tricks.com/Tutorial/linq/UXPF181012-SQL-Joins-with-C的荣誉

此枚举扩展允许您定义要排除的项列表和用于查找用于执行比较的键的函数。

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);

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

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

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

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

其次,使用“比较”属性来选择与所选内容不同的id

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

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

一旦你编写了一个泛型FuncEqualityComper,你就可以在任何地方使用它。

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);
}
{
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 });
}


var leftPeeps = peopleList2.Where(x => !peopleList1.Select(y => y.ID).Contains(x.ID))?.ToList() ?? new List<Person>();
}


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

注意!peopleList1.选择(y=>y.ID)。包含(x.ID)选择语句。这允许我们获取我们想要的索引器(ID)并查看它是否包含前一个列表的ID。!意味着我们不想要那些。这可能会让我们没有条目。所以,我们可以通过检查null和使用null合并来确保我们有一些东西。