LINQ 对特定属性的 Distinct()
我正在使用 LINQ 来了解它,但我不知道如何使用 Distinct
当我没有一个简单的列表时(一个简单的整数列表很容易做到,这不是问题)。 如果想在 List
的 一个 或更多 属性上使用 Distinct
,该怎么办? >TElement?
示例:如果对象是 Person
,具有属性 Id
。 如何获取所有 Person
并通过对象的属性 Id
对他们使用 Distinct
?
Person1: Id=1, Name="Test1"
Person2: Id=1, Name="Test1"
Person3: Id=2, Name="Test2"
我如何才能只获取 Person1
和 Person3
? 那可能吗?
如果 LINQ 无法实现,那么根据其某些属性获得 Person
列表的最佳方法是什么?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(23)
简单的! 您想将它们分组并从组中选出获胜者。
如果要在多个属性上定义组,操作方法如下:
注意:某些查询提供程序无法解析每个组必须至少有一个元素,并且 First 是在这种情况下调用的适当方法。 如果您发现自己正在与此类查询提供程序合作,FirstOrDefault 可能会帮助您通过查询提供程序获取查询。
注意 2:考虑 EF Core(EF Core 6 之前的版本)兼容方法的答案。 https://stackoverflow.com/a/66529949/8155
Simple! You want to group them and pick a winner out of the group.
If you want to define groups on multiple properties, here's how:
Note: Certain query providers are unable to resolve that each group must have at least one element, and that First is the appropriate method to call in that situation. If you find yourself working with such a query provider, FirstOrDefault may help get your query through the query provider.
Note2: Consider this answer for an EF Core (prior to EF Core 6) compatible approach. https://stackoverflow.com/a/66529949/8155
编辑:这现在是 MoreLINQ 的一部分。
您需要的是有效的“distinct-by”。 我不相信它是 LINQ 的一部分,尽管它相当容易编写:
因此,要仅使用
Id
属性查找不同的值,您可以使用:要使用多个属性,您可以使用匿名类型,它适当地实现了相等性:
未经测试,但它应该可以工作(并且现在至少可以编译)。
不过,它假定键的默认比较器 - 如果您想传递相等比较器,只需将其传递给
HashSet
构造函数即可。EDIT: This is now part of MoreLINQ.
What you need is a "distinct-by" effectively. I don't believe it's part of LINQ as it stands, although it's fairly easy to write:
So to find the distinct values using just the
Id
property, you could use:And to use multiple properties, you can use anonymous types, which implement equality appropriately:
Untested, but it should work (and it now at least compiles).
It assumes the default comparer for the keys though - if you want to pass in an equality comparer, just pass it on to the
HashSet
constructor.使用:
where
帮助您过滤条目(可能更复杂),groupby
和select
执行不同的功能。Use:
The
where
helps you filter the entries (could be more complex) and thegroupby
andselect
perform the distinct function.从 .NET 6 开始,有一个使用 Linq 中新的
DistinctBy()
扩展,因此我们可以执行以下操作:
DistinctBy
方法:Starting with .NET 6, there is new solution using the new
DistinctBy()
extension in Linq, so we can do:The signature of the
DistinctBy
method:如果您希望它看起来完全像 LINQ,您也可以使用查询语法:
You could also use query syntax if you want it to look all LINQ-like:
我认为这就足够了:
I think it is enough:
解决方案首先按您的字段进行分组,然后选择
FirstOrDefault
项。Solution first group by your fields then select
FirstOrDefault
item.您可以使用标准
Linq.ToLookup()
< /a>. 这将为每个唯一键创建值的集合。 只需选择集合中的第一项You can do this with the standard
Linq.ToLookup()
. This will create a collection of values for each unique key. Just select the first item in the collection以下代码在功能上等同于 Jon Skeet 的回答。
在 .NET 4.5 上测试,应该适用于任何早期版本的 LINQ。
顺便说一句,请在 Google 上查看 Jon Skeet 的最新版本 DistinctBy.cs代码。
更新 2022-04-03
根据 Andrew McClement 的评论,最好采用 John Skeet 的回答。
更新 2024-05-15
项目新家 MoreLINQ
The following code is functionally equivalent to Jon Skeet's answer.
Tested on .NET 4.5, should work on any earlier version of LINQ.
Incidentially, check out Jon Skeet's latest version of DistinctBy.cs on Google Code.
Update 2022-04-03
Based on an comment by Andrew McClement, best to take John Skeet's answer over this one.
Update 2024-05-15
New home of the project MoreLINQ
我写了一篇文章,解释如何扩展 Distinct 函数,以便您可以执行以下操作:
以下是该文章(现在位于网络存档中):扩展 LINQ - 在 Distinct 函数中指定属性< /em>
I've written an article that explains how to extend the Distinct function so that you can do as follows:
Here's the article (now in the Web Archive): Extending LINQ - Specifying a Property in the Distinct Function
我个人使用以下类:
然后,扩展方法:
最后,预期用途:
我发现使用此方法的优点是为接受
的其他方法重新使用 LambdaEqualityComparer 类IEqualityComparer
。 (哦,我把yield
的东西留给了原始的 LINQ 实现......)Personally I use the following class:
Then, an extension method:
Finally, the intended usage:
The advantage I found using this approach is the re-usage of
LambdaEqualityComparer
class for other methods that accept anIEqualityComparer
. (Oh, and I leave theyield
stuff to the original LINQ implementation...)您可以使用 DistinctBy() 通过对象属性获取不同记录。 使用前只需添加以下语句:
然后按如下方式使用它:
其中“索引”是我希望数据不同的属性。
You can use DistinctBy() for getting Distinct records by an object property. Just add the following statement before using it:
and then use it like following:
where 'Index' is the property on which i want the data to be distinct.
您可以这样做(尽管速度不是很快):
也就是说,“选择列表中不存在具有相同 ID 的其他人的所有人员”。
请注意,在您的示例中,这只会选择第 3 个人。我不知道如何从前两个人中找出您想要的人。
You can do it (albeit not lightning-quickly) like so:
That is, "select all people where there isn't another different person in the list with the same ID."
Mind you, in your example, that would just select person 3. I'm not sure how to tell which you want, out of the previous two.
如果您需要对多个属性使用不同的方法,您可以查看我的 PowerfulExtensions 库。 目前它还处于非常年轻的阶段,但您已经可以在任意数量的属性上使用 Distinct、Union、Intersect、Except 等方法;
这是你如何使用它:
In case you need a Distinct method on multiple properties, you can check out my PowerfulExtensions library. Currently it's in a very young stage, but already you can use methods like Distinct, Union, Intersect, Except on any number of properties;
This is how you use it:
当我们在项目中面临这样的任务时,我们定义了一个小的 API 来组成比较器。
所以,用例是这样的:
API 本身看起来像这样:
更多详细信息请参见我们的网站:LINQ 中的 IEqualityComparer。
When we faced such a task in our project we defined a small API to compose comparators.
So, the use case was like this:
And API itself looks like this:
More details is on our site: IEqualityComparer in LINQ.
如果您不想将 MoreLinq 库添加到项目中只是为了获得
DistinctBy
功能,那么您可以使用 Linq 的Distinct
方法的重载获得相同的最终结果:接受IEqualityComparer
参数。首先创建一个通用的自定义相等比较器类,该类使用 lambda 语法对通用类的两个实例执行自定义比较:
然后在主代码中像这样使用它:
瞧! :)
上面假设如下:
Person.Id
的类型为int
people
集合不包含任何 null 元素如果集合可以包含 null,然后简单地重写 lambda 来检查 null,例如:
编辑
这种方法与 Vladimir Nesterovsky 的答案中的方法类似,但更简单。
它也与 Joel 的答案类似,但允许涉及多个属性的复杂比较逻辑。
但是,如果您的对象只能在
Id
上有所不同,那么另一个用户给出了正确的答案,您需要做的就是覆盖GetHashCode()
和的默认实现>Equals()
在您的Person
类中,然后只需使用 Linq 的开箱即用的Distinct()
方法来过滤掉任何重复项。If you don't want to add the MoreLinq library to your project just to get the
DistinctBy
functionality then you can get the same end result using the overload of Linq'sDistinct
method that takes in anIEqualityComparer
argument.You begin by creating a generic custom equality comparer class that uses lambda syntax to perform custom comparison of two instances of a generic class:
Then in your main code you use it like so:
Voila! :)
The above assumes the following:
Person.Id
is of typeint
people
collection does not contain any null elementsIf the collection could contain nulls then simply rewrite the lambdas to check for null, e.g.:
EDIT
This approach is similar to the one in Vladimir Nesterovsky's answer but simpler.
It is also similar to the one in Joel's answer but allows for complex comparison logic involving multiple properties.
However, if your objects can only ever differ by
Id
then another user gave the correct answer that all you need to do is override the default implementations ofGetHashCode()
andEquals()
in yourPerson
class and then just use the out-of-the-boxDistinct()
method of Linq to filter out any duplicates.覆盖 Equals(object obj) 和 GetHashCode() 方法:
然后调用:
Override Equals(object obj) and GetHashCode() methods:
and then just call:
与其他 .NET 版本兼容的最佳方法是重写 Equals 和 GetHash 来处理此问题(请参阅 Stack Overflow 问题此代码返回不同的值。但是,我想要的是返回强类型集合而不是匿名类型),但如果您需要通用的东西在您的代码中,本文中的解决方案非常棒。
The best way to do this that will be compatible with other .NET versions is to override Equals and GetHash to handle this (see Stack Overflow question This code returns distinct values. However, what I want is to return a strongly typed collection as opposed to an anonymous type), but if you need something that is generic throughout your code, the solutions in this article are great.
您应该能够覆盖 person 上的 Equals,以实际对 Person.id 执行 Equals。 这应该会导致您所追求的行为。
You should be able to override Equals on person to actually do Equals on Person.id. This ought to result in the behavior you're after.
如果您使用旧的.NET版本,其中未内置扩展方法,那么您可以定义自己的扩展方法:
使用示例:
If you use old .NET version, where the extension method is not built-in, then you may define your own extension method:
Example of usage:
绝对不是最有效的,但对于那些正在寻找简短答案的人来说:
Definitely not the most efficient but for those, who are looking for a short and simple answer:
请使用下面的代码尝试一下。
Please give a try with below code.