Итак, вы просите ArgMinилиArgMax . C # не имеет встроенного API для тех.
Я искал чистый и эффективный (O (N) вовремя) способ сделать это. И я думаю, что нашел один:
Общая форма этого шаблона:
var min = data.Select(x => (key(x), x)).Min().Item2;
^ ^ ^
the sorting key | take the associated original item
Min by key(.)
Специально, используя пример в оригинальном вопросе:
Для C # 7.0 и выше, который поддерживает кортеж значения :
var youngest = people.Select(p => (p.DateOfBirth, p)).Min().Item2;
Для версии C # до 7.0 вместо этого может использоваться анонимный тип :
var youngest = people.Select(p => new { ppl = p; age = p.DateOfBirth }).Min().ppl;
Они работают , потому что оба значения кортежа и анонимный тип имеют осмысленные компараторов по умолчанию: для (x1, y1) и (x2, y2), она сначала сравнивает x1против x2, то y1против y2. Вот почему встроенный .Minможет быть использован на этих типах.
А так как анонимный тип и кортеж значения являются типами значения, они должны быть оба очень эффективными.
НОТА
В моих вышеприведенных ArgMinреализациях я предполагал DateOfBirthвзять тип DateTimeдля простоты и ясности. Исходный вопрос просит исключить эти записи с нулевым DateOfBirthполем:
Нулевым значениям DateOfBirth присвоено значение DateTime.MaxValue, чтобы исключить их из минимального рассмотрения (при условии, что хотя бы у одного указан указанный DOB).
Это может быть достигнуто с предварительной фильтрацией
people.Where(p => p.DateOfBirth.HasValue)
Так что это несущественно в вопросе реализации ArgMinили ArgMax.
ЗАМЕТКА 2
Приведенный выше подход имеет оговорку, что, когда два экземпляра имеют одинаковое минимальное значение, Min()реализация попытается сравнить эти экземпляры как прерыватели связей. Однако, если класс экземпляров не реализуется IComparable, будет выдана ошибка времени выполнения:
По крайней мере один объект должен реализовывать IComparable
К счастью, это все еще можно исправить довольно чисто. Идея состоит в том, чтобы связать отдаленный «идентификатор» с каждой записью, которая служит однозначным нарушителем связей. Мы можем использовать инкрементный идентификатор для каждой записи. Все еще используя возраст людей в качестве примера:
var youngest = Enumerable.Range(0, int.MaxValue)
.Zip(people, (idx, ppl) => (ppl.DateOfBirth, idx, ppl)).Min().Item3;