与 lambda 不同()?
是的,所以我有一个可枚举的值,并希望从中获得不同的值。
使用System.Linq
,当然还有一个名为Distinct
的扩展方法。 在简单的情况下,它可以不带参数使用,例如:
var distinctValues = myStringList.Distinct();
很好,但是如果我有一个需要指定相等性的可枚举对象,则唯一可用的重载是:
var distinctValues = myCustomerList.Distinct(someEqualityComparer);
相等比较器参数必须是IEqualityComparer
。 当然,我可以做到这一点,但它有点冗长而且笨拙。
我所期望的是一个需要 lambda 的重载,比如 Func
:
var distinctValues = myCustomerList.Distinct((c1, c2) => c1.CustomerId == c2.CustomerId);
有人知道是否存在这样的扩展,或者一些等效的解决方法? 或者我错过了什么?
或者,有没有办法指定内联的 IEqualityComparer
(让我尴尬)?
更新
我发现 Anders Hejlsberg 对 在 MSDN 论坛中发布有关此主题的帖子。 他说:
您将遇到的问题是,当两个对象进行比较时 相等,它们必须具有相同的 GetHashCode 返回值(否则 Distinct 内部使用的哈希表将无法正常工作)。 我们使用 IEqualityComparer 因为它封装兼容 将 Equals 和 GetHashCode 的实现集成到单个接口中。
我想这是有道理的。
Right, so I have an enumerable and wish to get distinct values from it.
Using System.Linq
, there's, of course, an extension method called Distinct
. In the simple case, it can be used with no parameters, like:
var distinctValues = myStringList.Distinct();
Well and good, but if I have an enumerable of objects for which I need to specify equality, the only available overload is:
var distinctValues = myCustomerList.Distinct(someEqualityComparer);
The equality comparer argument must be an instance of IEqualityComparer<T>
. I can do this, of course, but it's somewhat verbose and, well, cludgy.
What I would have expected is an overload that would take a lambda, say a Func<T, T, bool>
:
var distinctValues = myCustomerList.Distinct((c1, c2) => c1.CustomerId == c2.CustomerId);
Anyone know if some such extension exists, or some equivalent workaround? Or am I missing something?
Alternatively, is there a way of specifying an IEqualityComparer
inline (embarrass me)?
Update
I found a reply by Anders Hejlsberg to a post in an MSDN forum on this subject. He says:
The problem you're going to run into is that when two objects compare
equal they must have the same GetHashCode return value (or else the
hash table used internally by Distinct will not function correctly).
We use IEqualityComparer because it packages compatible
implementations of Equals and GetHashCode into a single interface.
I suppose that makes sense.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(20)
总结事情。 我认为大多数像我一样来到这里的人都希望获得最简单的解决方案,不使用任何库并且具有最佳的性能。
(我认为接受的分组方法在性能方面有点过分了。)
这是一个使用 IEqualityComparer 接口的简单扩展方法,该方法也适用于空值。
用法:
扩展方法代码
To Wrap things up . I think most of the people which came here like me want the simplest solution possible without using any libraries and with best possible performance.
(The accepted group by method for me i think is an overkill in terms of performance. )
Here is a simple extension method using the IEqualityComparer interface which works also for null values.
Usage:
Extension Method Code
在我看来,您想要
DistinctBy
来自 MoreLINQ。 然后您可以编写:这是
DistinctBy
的简化版本(没有无效检查,也没有指定您自己的键比较器的选项):It looks to me like you want
DistinctBy
from MoreLINQ. You can then write:Here's a cut-down version of
DistinctBy
(no nullity checking and no option to specify your own key comparer):从 .NET 6 或更高版本开始,有一个新的内置方法 Enumerable.DistinctBy 来实现这一点。
From .NET 6 or later, there is a new build-in method Enumerable.DistinctBy to achieve this.
这是一个简单的扩展方法,可以满足我的需要...
很遗憾他们没有将这样的独特方法烘焙到框架中,但是嘿嘿。
Here's a simple extension method that does what I need...
It's a shame they didn't bake a distinct method like this into the framework, but hey ho.
速记解法
Shorthand solution
不,没有这样的扩展方法重载。 我过去发现这让我自己很沮丧,因此我通常编写一个辅助类来处理这个问题。 目标是将
Func
转换为IEqualityComparer
。示例
这允许您编写以下内容
No there is no such extension method overload for this. I've found this frustrating myself in the past and as such I usually write a helper class to deal with this problem. The goal is to convert a
Func<T,T,bool>
toIEqualityComparer<T,T>
.Example
This allows you to write the following
您可以使用InlineComparer
使用示例:
来源:
https://stackoverflow.com/a/5969691/206730
使用 IEqualityComparer for Union
我可以内联指定显式类型比较器吗?
You can use InlineComparer
Usage sample:
Source:
https://stackoverflow.com/a/5969691/206730
Using IEqualityComparer for Union
Can I specify my explicit type comparator inline?
这会做你想要的,但我不知道性能:
至少它不冗长。
This will do what you want but I don't know about performance:
At least it's not verbose.
我用过的东西对我来说效果很好。
Something I have used which worked well for me.
我在这里看到的所有解决方案都依赖于选择已经可比较的字段。 不过,如果需要以不同的方式进行比较,这里的解决方案似乎通常适用于以下情况:
All solutions I've seen here rely on selecting an already comparable field. If one needs to compare in a different way, though, this solution here seems to work generally, for something like:
采用另一种方式:
序列返回不同的元素,通过属性 '_myCaustomerProperty' 比较它们。
Take another way:
The sequence return distinct elements compare them by property '_myCaustomerProperty' .
您可以使用 LambdaEqualityComparer:
You can use LambdaEqualityComparer:
实现此目的的一个棘手方法是使用
Aggregate()
扩展,使用字典作为累加器,并以 key-property 值作为键:以及 GroupBy-style< /em> 解决方案是使用
ToLookup()
:A tricky way to do this is use
Aggregate()
extension, using a dictionary as accumulator with the key-property values as keys:And a GroupBy-style solution is using
ToLookup()
:如果
Distinct()
没有产生唯一的结果,请尝试以下方法:If
Distinct()
doesn't produce unique results, try this one:IEnumerable
lambda 扩展:用法:
IEnumerable
lambda extension:Usage:
Microsoft System.Interactive 包 有一个采用键选择器 lambda 的 Distinct 版本。 这实际上与 Jon Skeet 的解决方案相同,但它可能有助于人们了解并查看库的其余部分。
The Microsoft System.Interactive package has a version of Distinct that takes a key selector lambda. This is effectively the same as Jon Skeet's solution, but it may be helpful for people to know, and to check out the rest of the library.
具体操作方法如下:
此方法允许您通过指定一个参数(如
.MyDistinct(d => d.Name)
)来使用它,但它也允许您指定一个具有条件作为第二个参数如下:NB 这还允许您指定其他函数,例如
.LastOrDefault(...)
。如果您只想公开条件,则可以通过将其实现为更简单:
在本例中,查询将如下所示:
NB 这里,表达式更简单,但请注意
.MyDistinct2
隐式使用.FirstOrDefault(...)
。注意:上面的示例使用以下演示类
Here's how you can do it:
This method allows you to use it by specifying one parameter like
.MyDistinct(d => d.Name)
, but it also allows you to specify a having condition as a second parameter like so:N.B. This would also allow you to specify other functions like for example
.LastOrDefault(...)
as well.If you want to expose just the condition, you can have it even simpler by implementing it as:
In this case, the query would just look like:
N.B. Here, the expression is simpler, but note
.MyDistinct2
uses.FirstOrDefault(...)
implicitly.Note: The examples above are using the following demo class
我假设您有一个
IEnumerable
,并且在您的示例委托中,您希望c1
和c2
引用两个该列表中的元素?我相信你可以通过自连接来实现这一点:
I'm assuming you have an
IEnumerable<T>
, and in your example delegate, you would likec1
andc2
to be referring to two elements in this list?I believe you could achieve this with a self join:
我发现这是最简单的解决方案。
I found this as the easiest solution.