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.


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.


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;
        }
    }
}

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.


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());

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ę.


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();

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 });

-2

wypróbuj ten kod:

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

-6

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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.