Mam obiekt Person z właściwością Nullable DateOfBirth. Czy istnieje sposób użycia LINQ do przeszukania listy obiektów Person dla obiektu o najwcześniejszej / najmniejszej wartości DateOfBirth.
Oto, od czego zacząłem:
var firstBornDate = People.Min(p => p.DateOfBirth.GetValueOrDefault(DateTime.MaxValue));
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).
Ale dla mnie wszystkim jest ustawienie firstBornDate na wartość DateTime. Chciałbym uzyskać obiekt Person, który do niego pasuje. Czy muszę napisać drugie zapytanie w ten sposób:
var firstBorn = People.Single(p=> (p.DateOfBirth ?? DateTime.MaxValue) == firstBornDate);
A może istnieje prostszy sposób na zrobienie tego?
a.Min(x => x.foo);
max("find a word of maximal length in this sentence".split(), key=len)
zwraca ciąg „zdanie”. W języku C #"find a word of maximal length in this sentence".Split().Max(word => word.Length)
oblicza, że 8 jest najdłuższa długość każdego słowa, ale nie powiem ci, co najdłuższa słowo jest .Odpowiedzi:
źródło
curMin == null
?curMin
może być tylkonull
wtedy, gdy używaszAggregate()
z ziarnem, które jestnull
.Niestety nie ma wbudowanej metody, ale jest to dość łatwe do wdrożenia dla siebie. Oto jego zalety:
Przykładowe użycie:
Zauważ, że spowoduje to zgłoszenie wyjątku, jeśli sekwencja jest pusta, i zwróci pierwszy element o minimalnej wartości, jeśli jest więcej niż jeden.
Alternatywnie możesz użyć implementacji, którą mamy w MoreLINQ , w MinBy.cs . (Oczywiście istnieje odpowiednik
MaxBy
).Zainstaluj za pomocą konsoli menedżera pakietów:
źródło
UWAGA: Załączam tę odpowiedź dla kompletności, ponieważ PO nie wspomniał o tym, jakie jest źródło danych i nie powinniśmy przyjmować żadnych założeń.
To zapytanie daje poprawną odpowiedź, ale może być wolniejsze, ponieważ może być konieczne sortowanie wszystkich elementów, w
People
zależności od struktury danychPeople
:AKTUALIZACJA: Właściwie nie powinienem nazywać tego rozwiązania „naiwnym”, ale użytkownik musi wiedzieć, o co pyta. „Powolność” tego rozwiązania zależy od podstawowych danych. Jeśli jest to tablica lub
List<T>
, wówczas LINQ do obiektów nie ma innego wyboru, jak najpierw posortować całą kolekcję przed wybraniem pierwszego elementu. W takim przypadku będzie on wolniejszy niż inne sugerowane rozwiązanie. Jeśli jednak jest to tabela LINQ do SQL iDateOfBirth
kolumna indeksowana, wówczas SQL Server użyje indeksu zamiast sortować wszystkie wiersze. Inne niestandardoweIEnumerable<T>
implementacje mogą również korzystać z indeksów (patrz i4o: Indeksowany LINQ lub obiektowa baza danych db4o ) i sprawić, że to rozwiązanie będzie szybsze niżAggregate()
lubMaxBy()
/MinBy()
które muszą raz iterować całą kolekcję. W rzeczywistości LINQ do Objects mógł (teoretycznie) zrobić specjalne przypadkiOrderBy()
dla posortowanych kolekcji takich jakSortedList<T>
, ale o ile mi wiadomo, nie robi tego.źródło
Zrobiłby lewę
źródło
Więc prosisz o
ArgMin
lubArgMax
. 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:
Szczególnie, korzystając z przykładu z oryginalnego pytania:
W wersji C # 7.0 i nowszej obsługującej krotkę wartości :
W wersji C # wcześniejszej niż 7.0 można użyć zamiast tego typu anonimowego :
Pracują ponieważ zarówno krotka wartość i typ anonimowy mieć sensowne domyślne porównywarki: dla (x1, y1) i (x2, y2), najpierw porównuje
x1
vsx2
, następniey1
vsy2
. Dlatego wbudowanego.Min
moż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
ArgMin
implementacjach przyjąłemDateOfBirth
typDateTime
dla uproszczenia i przejrzystości. Pierwotne pytanie dotyczy wykluczenia tych wpisów o pustymDateOfBirth
polu:Można to osiągnąć dzięki filtrowaniu wstępnemu
Jest to więc nieistotne dla kwestii wdrożenia
ArgMin
lubArgMax
.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 zaimplementowanaIComparable
, zostanie wygenerowany błąd środowiska wykonawczego: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:
źródło
Rozwiązanie bez dodatkowych pakietów:
możesz także owinąć go w rozszerzenie:
iw tym przypadku:
Nawiasem mówiąc ... O (n ^ 2) nie jest najlepszym rozwiązaniem. Paul Betts dał tłustsze rozwiązanie niż moje. Ale moje wciąż jest rozwiązaniem LINQ i jest prostsze i krótsze niż inne rozwiązania tutaj.
źródło
źródło
Idealnie proste użycie agregatu (odpowiednik fold w innych językach):
Jedynym minusem jest to, że dostęp do właściwości uzyskuje się dwa razy na element sekwencji, co może być kosztowne. Trudno to naprawić.
źródło
Poniżej przedstawiono bardziej ogólne rozwiązanie. Zasadniczo robi to samo (w kolejności O (N)), ale na dowolnych typach IEnumerable i może mieszać się z typami, których selektory właściwości mogą zwracać null.
Testy:
źródło
EDYTUJ ponownie:
Przepraszam. Poza brakiem wartości zerowej patrzyłem na niewłaściwą funkcję,
Min <(Of <(TSource, TResult>)>) (IEnumerable <(Of <(TSource>)>), Func <(Of <(TSource, TResult>)>)) zwraca typ wyniku, jak powiedziałeś.
Powiedziałbym, że jednym z możliwych rozwiązań jest wdrożenie IComparable i użycie Min <(Of <(TSource>)>) (IEnumerable <(Of <(TSource>)>)) , co tak naprawdę zwraca element z IEnumerable. Oczywiście to nie pomoże, jeśli nie możesz zmodyfikować elementu. Uważam, że projekt MS jest trochę dziwny.
Oczywiście zawsze możesz zrobić pętlę for, jeśli potrzebujesz, lub użyć implementacji MoreLINQ, którą dał Jon Skeet.
źródło
Inna implementacja, która może działać z zerowanymi kluczami selektora i dla kolekcji typu odwołania zwraca null, jeśli nie znaleziono odpowiednich elementów. Może to być pomocne na przykład w przypadku przetwarzania wyników bazy danych.
Przykład:
źródło
Sam szukałem czegoś podobnego, najlepiej bez korzystania z biblioteki lub sortowania całej listy. Moje rozwiązanie skończyło się podobnie jak samo pytanie, tylko trochę uproszczone.
źródło
var min = People.Min(...); var firstBorn = People.FirstOrDefault(p => p.DateOfBirth == min...
W przeciwnym razie dostajemy minę wielokrotnie, aż znajdzie tę, której szukasz.