我有一个具有Nullable DateOfBirth属性的Person对象 . 有没有办法使用LINQ查询具有最早/最小DateOfBirth值的Person对象列表 .
这是我开始的:
var firstBornDate = People.Min(p => p.DateOfBirth.GetValueOrDefault(DateTime.MaxValue));
Null DateOfBirth值设置为DateTime.MaxValue,以便将它们排除在Min考虑范围之外(假设至少有一个具有指定的DOB) .
但对我来说,所有这一切都是将firstBornDate设置为DateTime值 . 我想得到的是与之匹配的Person对象 . 我是否需要编写第二个查询:
var firstBorn = People.Single(p=> (p.DateOfBirth ?? DateTime.MaxValue) == firstBornDate);
或者有更精简的方法吗?
12 回答
没有检查,但这必须做预期的事情:
和分钟:
只检查过Entity framework 6.0.0>:这可以完成:
不幸的是,没有内置的方法来做到这一点 .
或者,您可以使用我们在MoreLINQ中获得的实现,在MinBy.cs中 . (当然还有一个相应的
MaxBy
. )以下是它的内容:请注意,如果序列为空,这将抛出异常,如果有多个,则返回具有最小值的第一个元素 .
注意:由于OP未提及数据源是什么而且我们不应做出任何假设,因此我将此答案包括在内以便完整 .
此查询给出了正确的答案,但可能会更慢,因为它可能必须对
People
中的所有项进行排序,具体取决于People
的数据结构:更新:实际上我不应该't call this solution 755808 , but the user does need to know what he is querying against. This solution' s "slowness"取决于基础数据 . 如果这是一个数组或
List<T>
,则LINQ to Objects别无选择,只能在选择第一个项目之前先对整个集合进行排序 . 在这种情况下,它将比建议的其他解决方案慢 . 但是,如果这是一个LINQ to SQL表,并且DateOfBirth
是一个索引列,则SQL Server将使用索引而不是对所有行进行排序 . 其他自定义IEnumerable<T>
实现也可以使用索引(请参阅i4o: Indexed LINQ或对象数据库db4o)并使此解决方案比Aggregate()
或MaxBy()
/MinBy()
更快,这需要迭代整个集合一次 . 实际上,LINQ to Objects可以(在理论上)在OrderBy()
中为SortedList<T>
这样的已排序集合创建特殊情况,但据我所知,它并没有 .会做的伎俩
所以你要求
ArgMin
或ArgMax
. C#没有内置的API .我一直在寻找干净,高效(O(n)及时)的方式来做到这一点 . 我想我发现了一个:
这种模式的一般形式是:
特别是,使用原始问题中的示例:
For C# 7.0 and above that supports value tuple:
For C# version before 7.0, anonymous type can be used instead:
它们有效,因为值元组和匿名类型都有合理的默认比较器:对于(x1,y1)和(x2,y2),它首先比较
x1
vsx2
,然后是y1
vsy2
. 这就是内置.Min
可用于这些类型的原因 .由于匿名类型和值元组都是值类型,因此它们应该非常有效 .
NOTE
在我上面的
ArgMin
实现中,为了简单和清晰起见,我假设DateOfBirth
采用DateTime
类型 . 原始问题要求使用nullDateOfBirth
字段排除这些条目:它可以通过预过滤实现
因此,实施
ArgMin
或ArgMax
的问题并不重要 .NOTE 2
上面的方法有一个警告,当有两个实例具有相同的最小值时,
Min()
实现将尝试将实例作为打破平局进行比较 . 但是,如果实例的类未实现IComparable
,则将引发运行时错误:幸运的是,这仍然可以相当干净地修复 . 这个想法是将一个分散的“ID”与作为明确的打破平局的每个条目联系起来 . 我们可以为每个条目使用增量ID . 仍以人口年龄为例:
以下是更通用的解决方案 . 它基本上做同样的事情(以O(N)顺序)但是在任何IEnumberable类型上并且可以与其属性选择器可以返回null的类型混合 .
测试:
再次编辑:
抱歉 . 除了错过可空之外,我正在寻找错误的功能,
Min<(Of <(TSource, TResult>)>)(IEnumerable<(Of <(TSource>)>), Func<(Of <(TSource, TResult>)>))会按照您的说法返回结果类型 .
我想说一个可能的解决方案是实现IComparable并使用Min<(Of <(TSource>)>)(IEnumerable<(Of <(TSource>)>)),它确实从IEnumerable返回一个元素 . 当然,这不会修改元素 . 我发现MS的设计在这里有点奇怪 .
当然,如果需要,你总是可以进行for循环,或者使用更多LINQ实施Jon Skeet给出了 .
我自己也在寻找类似的东西,最好不要使用库或整理整个列表 . 我的解决方案最终类似于问题本身,只是简化了一点 .
要从对象数组中获取属性的最大值或最小值:
创建一个存储每个属性值的列表:
将所有属性值添加到列表:
从列表中获取最大值或最小值:
现在,您可以循环遍历对象数组,并将要检查的属性值与max或min int进行比较: