Wyraźne w Linq na podstawie tylko jednego pola tabeli

138

Próbuję użyć .distinct w Linq, aby uzyskać wynik na podstawie jednego pola tabeli (więc nie wymagają całego zduplikowanych rekordów z tabeli).

Wiem, jak pisać podstawowe zapytania, używając odrębnych:

var query = (from r in table1
orderby r.Text
select r).distinct();

ale potrzebuję wyników, które r.textnie są zduplikowane.

Megha Jain
źródło
Musisz określić, które pole ma być odrębne, patrz msdn.microsoft.com/en-us/library/bb348436.aspx
Antarr Byrd

Odpowiedzi:

308

Spróbuj tego:

table1.GroupBy(x => x.Text).Select(x => x.FirstOrDefault());

Spowoduje to pogrupowanie tabeli według Texti użycie pierwszego wiersza z każdej grupy, co spowoduje utworzenie wierszy, w których Textjest inny.

Daniel Hilgarth
źródło
2
A co jeśli Groupby ma więcej niż 1 pole?
6
@ user585440: W takim przypadku używasz typu anonimowego, takiego jak:table1.GroupBy(x => new { x.Text, x.Property2, x.Property3 }).Select(x => x.First());
Daniel Hilgarth,
2
Tak, masz rację i już to znalazłem. W każdym razie dzięki. Uważam również, że Select (x => x.First ()) może powodować awarię. Lepiej jest zmienić na Select (x => x.FirstOrDefault ());
6
Musiałem użyć FirstOrDefault, bo inaczej wystąpił błąd wykonania
TruthOf42
2
@ TruthOf42 To raczej mało prawdopodobne. GroupBynie tworzy pustych grup, zobacz mój poprzedni komentarz. Najprawdopodobniej twój kod zawiera więcej niż to, co tutaj widzisz. Może masz Whererównież lub warunek dla First.
Daniel Hilgarth
26

MoreLinq ma metodę DistinctBy , której możesz użyć:

Pozwoli Ci to na:

var results = table1.DistictBy(row => row.Text);

Implementacja metody (bez walidacji argumentów) jest następująca:

private static IEnumerable<TSource> DistinctByImpl<TSource, TKey>(IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
{
    HashSet<TKey> knownKeys = new HashSet<TKey>(comparer);
    foreach (TSource element in source)
    {
        if (knownKeys.Add(keySelector(element)))
        {
            yield return element;
        }
    }
}
Servy
źródło
1
przepraszam, nie chciałem używać equalityComparer.
Megha Jain
@MeghaJain Cóż, jeden będzie używany niezależnie, jak również GroupBypotrzebuje. Obie metody będą używać wartości domyślnej, EqualityComparerjeśli żadna nie zostanie podana.
Servy
9
Cóż, popraw mnie, jeśli się mylę, ale to wyraźne tutaj jest zrobione w pamięci, a nie w DB? Czy to nie może doprowadzić do niepożądanego pełnego skanowania?
Kek
@Kek. Nie, ze względu na zwrot z zysku zatrzymasz się na pierwszym odrębnym elemencie. Ostatecznie tak, załadujesz każdy klucz do HashSet, ale ponieważ jest on IEnumerable in i IEnumerable out, otrzymasz tylko te elementy. Jeśli mówisz o LINQ to SQL, to tak, spowoduje to skanowanie tabeli.
PRMan
14

ale potrzebuję wyników, w których r.text nie jest zduplikowany

Brzmi, jakbyś tego chciała:

table1.GroupBy(x => x.Text)
      .Where(g => g.Count() == 1)
      .Select(g => g.First());

Spowoduje to wybranie wierszy, w których Textjest unikalny.

Tim Schmelter
źródło
8

Powyższa odpowiedź Daniela Hilgartha prowadzi do System.NotSupportedwyjątku With Entity-Framework . W przypadku Entity-Framework musi to być:

table1.GroupBy(x => x.Text).Select(x => x.FirstOrDefault());
Biraj Saha
źródło
3

Na ten temat toczy się wiele dyskusji.

Możesz znaleźć jeden z nich tutaj :

Jedną z najpopularniejszych sugestii jest metoda Distinct, przyjmująca wyrażenie lambda jako parametr, jak wskazał @Servy.

Główny architekt C #, Anders Hejlsberg, zaproponował tutaj rozwiązanie . Wyjaśnienie również, dlaczego zespół projektowy frameworka zdecydował się nie dodawać przeciążenia metody Distinct, która pobiera lambdę.

TKharaishvili
źródło
2

Z tego, co znalazłem, Twoje zapytanie jest w większości poprawne. Wystarczy zmienić „select r” na „select r.Text” i to powinno rozwiązać problem. W ten sposób MSDN udokumentowało, jak powinno działać.

Dawny:

    var query = (from r in table1 orderby r.Text select r.Text).distinct();
Josh Parks
źródło
zmieniliście polecenie „wybierz”, które może nie być pożądane w tym przypadku
faza
1
data.Select(x=>x.Name).Distinct().Select(x => new SelectListItem { Text = x });
bgS
źródło
-2

wypróbuj ten kod:

table1.GroupBy(x => x.Text).Select(x => x.FirstOrDefault());
HamidReza
źródło
-6

Możesz spróbować tego:table1.GroupBy(t => t.Text).Select(shape => shape.r)).Distinct();

LucaGuerra
źródło