Таким чином , ви просите 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, буде викинута помилка виконання:
Принаймні один об’єкт повинен реалізовувати Ізрівнянний
На щастя, це все ще можна виправити досить чисто. Ідея полягає в тому, щоб пов'язати дистанційну "ідентифікацію" з кожним записом, який служить однозначним вимикачем. Ми можемо використовувати додатковий ідентифікатор для кожного запису. Досі користуються віком людей як приклад:
var youngest = Enumerable.Range(0, int.MaxValue)
.Zip(people, (idx, ppl) => (ppl.DateOfBirth, idx, ppl)).Min().Item3;