Więc prosisz o ArgMinlub ArgMax. C # nie ma dla nich wbudowanego API.
Szukałem czystego i wydajnego (O (n) na czas) sposobu, aby to zrobić. I myślę, że znalazłem jeden:
Ogólna forma tego wzoru to:
var min = data.Select(x => (key(x), x)).Min().Item2;
^ ^ ^
the sorting key | take the associated original item
Min by key(.)
Szczególnie, korzystając z przykładu z oryginalnego pytania:
W wersji C # 7.0 i nowszej obsługującej krotkę wartości :
var youngest = people.Select(p => (p.DateOfBirth, p)).Min().Item2;
W wersji C # wcześniejszej niż 7.0 można użyć zamiast tego typu anonimowego :
var youngest = people.Select(p => new { ppl = p; age = p.DateOfBirth }).Min().ppl;
Pracują ponieważ zarówno krotka wartość i typ anonimowy mieć sensowne domyślne porównywarki: dla (x1, y1) i (x2, y2), najpierw porównuje x1vs x2, następnie y1vs y2. Dlatego wbudowanego .Minmożna używać na tych typach.
A ponieważ zarówno anonimowy typ, jak i krotka wartości są typami wartości, oba powinny być bardzo wydajne.
UWAGA
W moich powyższych ArgMinimplementacjach przyjąłem DateOfBirthtyp DateTimedla uproszczenia i przejrzystości. Pierwotne pytanie dotyczy wykluczenia tych wpisów o pustym DateOfBirthpolu:
Wartości Null DateOfBirth są ustawiane na DateTime.MaxValue, aby wykluczyć je z uwzględnienia wartości minimalnej (zakładając, że co najmniej jedna ma określoną DOB).
Można to osiągnąć dzięki filtrowaniu wstępnemu
people.Where(p => p.DateOfBirth.HasValue)
Jest to więc nieistotne dla kwestii wdrożenia ArgMinlub ArgMax.
UWAGA 2
Powyższe podejście ma zastrzeżenie, że jeśli istnieją dwa wystąpienia, które mają tę samą wartość minimalną, wówczas Min()implementacja spróbuje porównać wystąpienia jako rozstrzygające. Jeśli jednak klasa instancji nie zostanie zaimplementowana IComparable, zostanie wygenerowany błąd środowiska wykonawczego:
Co najmniej jeden obiekt musi implementować IComparable
Na szczęście można to nadal naprawić dość czysto. Chodzi o to, aby skojarzyć odrębny „identyfikator” z każdym wpisem, który służy jako jednoznaczny element rozstrzygający. Możemy użyć przyrostowego identyfikatora dla każdego wpisu. Nadal wykorzystując wiek osób jako przykład:
var youngest = Enumerable.Range(0, int.MaxValue)
.Zip(people, (idx, ppl) => (ppl.DateOfBirth, idx, ppl)).Min().Item3;