我有一个具有Nullable DateOfBirth属性的Person对象。是否有一种方法可以使用LINQ来查询Person对象列表中最早/最小的DateOfBirth值?
这是我的开场白:
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);
或者有没有更精简的方法?
通过IEnumerable上的扩展函数返回对象和找到的最小值的方法。它接受一个可以对集合中的对象执行任何操作的Func:
public static (double min, T obj) tMin<T>(this IEnumerable<T> ienum,
Func<T, double> aFunc)
{
var okNull = default(T);
if (okNull != null)
throw new ApplicationException("object passed to Min not nullable");
(double aMin, T okObj) best = (double.MaxValue, okNull);
foreach (T obj in ienum)
{
double q = aFunc(obj);
if (q < best.aMin)
best = (q, obj);
}
return (best);
}
例子,对象是一个机场,我们想找到最近的机场到给定的(纬度,经度)。机场有纬度(纬度、纬度)功能。
(double okDist, Airport best) greatestPort = airPorts.tMin(x => x.dist(okLat, okLon));
另一种实现,可以使用可空的选择器键,如果没有找到合适的元素,则对于引用类型集合返回null。
例如,这对处理数据库结果很有帮助。
public static class IEnumerableExtensions
{
/// <summary>
/// Returns the element with the maximum value of a selector function.
/// </summary>
/// <typeparam name="TSource">The type of the elements of source.</typeparam>
/// <typeparam name="TKey">The type of the key returned by keySelector.</typeparam>
/// <param name="source">An IEnumerable collection values to determine the element with the maximum value of.</param>
/// <param name="keySelector">A function to extract the key for each element.</param>
/// <exception cref="System.ArgumentNullException">source or keySelector is null.</exception>
/// <exception cref="System.InvalidOperationException">source contains no elements.</exception>
/// <returns>The element in source with the maximum value of a selector function.</returns>
public static TSource MaxBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) => MaxOrMinBy(source, keySelector, 1);
/// <summary>
/// Returns the element with the minimum value of a selector function.
/// </summary>
/// <typeparam name="TSource">The type of the elements of source.</typeparam>
/// <typeparam name="TKey">The type of the key returned by keySelector.</typeparam>
/// <param name="source">An IEnumerable collection values to determine the element with the minimum value of.</param>
/// <param name="keySelector">A function to extract the key for each element.</param>
/// <exception cref="System.ArgumentNullException">source or keySelector is null.</exception>
/// <exception cref="System.InvalidOperationException">source contains no elements.</exception>
/// <returns>The element in source with the minimum value of a selector function.</returns>
public static TSource MinBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) => MaxOrMinBy(source, keySelector, -1);
private static TSource MaxOrMinBy<TSource, TKey>
(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, int sign)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (keySelector == null) throw new ArgumentNullException(nameof(keySelector));
Comparer<TKey> comparer = Comparer<TKey>.Default;
TKey value = default(TKey);
TSource result = default(TSource);
bool hasValue = false;
foreach (TSource element in source)
{
TKey x = keySelector(element);
if (x != null)
{
if (!hasValue)
{
value = x;
result = element;
hasValue = true;
}
else if (sign * comparer.Compare(x, value) > 0)
{
value = x;
result = element;
}
}
}
if ((result != null) && !hasValue)
throw new InvalidOperationException("The source sequence is empty");
return result;
}
}
例子:
public class A
{
public int? a;
public A(int? a) { this.a = a; }
}
var b = a.MinBy(x => x.a);
var c = a.MaxBy(x => x.a);