Таким чином , ви просите 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;