Tak, więc mam wyliczenie i chcę uzyskać od niego odrębne wartości.
Używając System.Linq
, istnieje oczywiście metoda rozszerzenia o nazwie Distinct
. W prostym przypadku można go używać bez parametrów, takich jak:
var distinctValues = myStringList.Distinct();
Dobrze i dobrze, ale jeśli mam wyliczenie obiektów, dla których muszę określić równość, jedynym dostępnym przeciążeniem jest:
var distinctValues = myCustomerList.Distinct(someEqualityComparer);
Argument porównania równości musi być instancją klasy IEqualityComparer<T>
. Mogę to oczywiście zrobić, ale jest to trochę gadatliwe i, no cóż, niezgrabne.
Spodziewałbym się przeciążenia, które zajęłoby lambda, powiedzmy Func <T, T, bool>:
var distinctValues
= myCustomerList.Distinct((c1, c2) => c1.CustomerId == c2.CustomerId);
Czy ktoś wie, czy istnieje jakieś takie rozszerzenie lub jakieś równoważne obejście? A może coś mi brakuje?
Alternatywnie, czy istnieje sposób na określenie inline IEqualityComparer (zawstydza mnie)?
Aktualizacja
Znalazłem odpowiedź Andersa Hejlsberga na post na forum MSDN na ten temat. On mówi:
Problem, na który natkniesz się, polega na tym, że gdy dwa obiekty są równe, muszą mieć tę samą wartość zwracaną GetHashCode (w przeciwnym razie tabela skrótów używana wewnętrznie przez Distinct nie będzie działać poprawnie). Używamy IEqualityComparer, ponieważ łączy on kompatybilne implementacje Equals i GetHashCode w jednym interfejsie.
Myślę, że to ma sens ...
źródło
.Distinct(new KeyEqualityComparer<Customer,string>(c1 => c1.CustomerId))
, i wyjaśnić, dlaczego GetHashCode () jest ważne, aby działać poprawnie.Odpowiedzi:
źródło
DistinctBy
(a nawetDistinct
, ponieważ podpis będzie unikalny).yield
instrukcji, więc przesyłanie strumieniowe nie jest technicznie możliwe. Dziękuję za odpowiedź. Użyję go podczas kodowania w C #. ;-)Wygląda mi na to, że chcesz
DistinctBy
od MoreLINQ . Następnie możesz napisać:Oto skrócona wersja
DistinctBy
(bez sprawdzania nieważności i bez opcji określania własnego modułu porównującego klucze):źródło
yield
+ dodatkowej lib, foreach można ponownie napisać jakoreturn source.Where(element => knownKeys.Add(keySelector(element)));
Podsumowując . Myślę, że większość ludzi, którzy tu przybyli, jak ja, chce najprostszego możliwego rozwiązania bez użycia bibliotek i najlepszej możliwej wydajności .
(Uważam, że grupa zaakceptowana metodą według mnie jest przesadą pod względem wydajności.)
Oto prosta metoda rozszerzenia wykorzystująca interfejs IEqualityComparer , który działa również dla wartości null.
Stosowanie:
Kod metody rozszerzenia
źródło
Nie, nie ma takiego przeciążenia metody rozszerzenia. To mnie frustrowało w przeszłości i jako takie zwykle piszę klasę pomocniczą, aby poradzić sobie z tym problemem. Celem jest konwersja
Func<T,T,bool>
naIEqualityComparer<T,T>
.Przykład
To pozwala napisać następujące
źródło
IEqualityComparer<T>
z projekcji: stackoverflow.com/questions/188120/…Skrócone rozwiązanie
źródło
To zrobi, co chcesz, ale nie wiem o wydajności:
Przynajmniej to nie jest pełne.
źródło
Oto prosta metoda rozszerzenia, która robi to, czego potrzebuję ...
Szkoda, że nie upiekli takiej metody w ramach, ale hej, ho.
źródło
x.Key
sięx.First()
i zmienić wartość powrotną doIEnumerable<T>
Coś, co wykorzystałem, które działało dobrze dla mnie.
źródło
Wszystkie rozwiązania, które tu widziałem, polegają na wyborze już porównywalnego pola. Jeśli jednak trzeba porównać inaczej, wydaje się , że to rozwiązanie działa ogólnie w przypadku czegoś takiego:
źródło
Wybierz inny sposób:
Sekwencja zwraca różne elementy, które porównują je według właściwości „_myCaustomerProperty”.
źródło
Możesz użyć InlineComparer
Przykład użycia :
Źródło: https://stackoverflow.com/a/5969691/206730
Korzystanie z IEqualityComparer dla Union
Czy mogę podać własny komparator typu jawnego?
źródło
Możesz użyć LambdaEqualityComparer:
źródło
Trudnym sposobem na to jest użycie
Aggregate()
rozszerzenia, używając słownika jako akumulatora z wartościami klucz-właściwość jako kluczami:A rozwiązanie w stylu GroupBy wykorzystuje
ToLookup()
:źródło
Dictionary<int, Customer>
zamiast tego?Zakładam, że masz IEnumerable, aw swoim przykładowym delegacie chciałbyś, aby c1 i c2 odnosiły się do dwóch elementów na tej liście?
Wierzę, że można to osiągnąć za pomocą samodzielnego łączenia var differentResults = from c1 in myList join c2 in myList on
źródło
Jeśli
Distinct()
nie da unikalnych wyników, spróbuj tego:źródło
Pakiet Microsoft System.Interactive zawiera wersję Distinct, która przyjmuje klucz lambda selektora kluczy. Jest to faktycznie to samo, co rozwiązanie Jona Skeeta, ale może być pomocne dla ludzi, aby wiedzieć i sprawdzić resztę biblioteki.
źródło
Oto jak możesz to zrobić:
Ta metoda pozwala na użycie go poprzez określenie jednego parametru
.MyDistinct(d => d.Name)
, ale pozwala również określić warunek posiadania jako drugi parametr, taki jak:Uwaga: Pozwoli to również na określenie innych funkcji, takich jak na przykład
.LastOrDefault(...)
.Jeśli chcesz ujawnić tylko warunek, możesz go uprościć, implementując go jako:
W takim przypadku zapytanie wyglądałoby tak:
Uwaga: Wyrażenie jest tutaj prostsze, ale nuta
.MyDistinct2
używa.FirstOrDefault(...)
domyślnie.Uwaga: powyższe przykłady wykorzystują następującą klasę demonstracyjną
źródło
IEnumerable
rozszerzenie lambda:Stosowanie:
źródło