我有一个具有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);

或者有没有更精简的方法?


当前回答

People.Aggregate((curMin, x) => (curMin == null || (x.DateOfBirth ?? DateTime.MaxValue) <
    curMin.DateOfBirth ? x : curMin))

其他回答

. net 6原生支持MaxBy/MinBy。你可以用简单的

人。MinBy(p => p.出生日期)

你可以像SQL中的order by和limit/fetch一样。按出生日期递增排序,然后取第一行。

var query = from person in People
            where person.DateOfBirth!=null
            orderby person.DateOfBirth
            select person;
var firstBorn = query.Take(1).toList();

从。net 6 (Preview 7)或更高版本开始,有了新的内置方法Enumerable。MaxBy和Enumerable。MinBy来实现这一点。

var lastBorn = people.MaxBy(p => p.DateOfBirth);

var firstBorn = people.MinBy(p => p.DateOfBirth);

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