2010-12-14 8 views
9

Do niedawna używałem elementu Distinct w LINQ, aby wybrać odrębną kategorię (wyliczenie) z tabeli. To działało dobrze.Używanie Distinct z LINQ i obiektami

Teraz muszę mieć go odrębny w klasie zawierającej kategorię i kraj (oba wyliczenia). The Distinct nie działa teraz.

Co robię źle?

Odpowiedz

13

Uważam ten post wyjaśnia problem: http://blog.jordanterrell.com/post/LINQ-Distinct()-does-not-work-as-expected.aspx

Zawartość powyższego linku można podsumować stwierdzeniem, że metoda Wyraźny() może być zastąpiony w następujący sposób.

var distinctItems = items 
     .GroupBy(x => x.PropertyToCompare) 
     .Select(x => x.First()); 
+0

To działało świetnie. Dzięki. –

+0

Proszę nie odpowiadać, po prostu łącząc. Uwzględniono odpowiedni kod w odpowiedzi. –

+1

Tak, już tego nie robię, ale w 2010 roku nie zwracałem uwagi na tę praktykę. – Stilgar

4

spróbować IQualityComparer

public class MyObjEqualityComparer : IEqualityComparer<MyObj> 
{ 
    public bool Equals(MyObj x, MyObj y) 
    { 
     return x.Category.Equals(y.Category) && 
       x.Country.Equals(y.Country); 
    } 

    public int GetHashCode(MyObj obj) 
    { 
     return obj.GetHashCode(); 
    } 
} 

następnie użyć tutaj

var comparer = new MyObjEqualityComparer(); 
myObjs.Where(m => m.SomeProperty == "whatever").Distinct(comparer); 
+0

Podoba mi się to rozwiązanie, jednak miałem z tym problem. GetHashCode powodował, że nie znalazł pasujących elementów. Musiałem zmienić to na coś w stylu 'return obj.Category.GetHashCode() + obj.Country.GetHashCode()' –

3

Dla wyjaśnienia, spojrzeć na inne odpowiedzi. Podaję tylko jeden sposób na poradzenie sobie z tym problemem.

może Lubisz this:

public class LambdaComparer<T>:IEqualityComparer<T>{ 
    private readonly Func<T,T,bool> _comparer; 
    private readonly Func<T,int> _hash; 
    public LambdaComparer(Func<T,T,bool> comparer): 
    this(comparer,o=>0) {} 
    public LambdaComparer(Func<T,T,bool> comparer,Func<T,int> hash){ 
    if(comparer==null) throw new ArgumentNullException("comparer"); 
    if(hash==null) throw new ArgumentNullException("hash"); 
    _comparer=comparer; 
    _hash=hash; 
    } 
    public bool Equals(T x,T y){ 
    return _comparer(x,y); 
    } 
    public int GetHashCode(T obj){ 
    return _hash(obj); 
    } 
} 

Zastosowanie:

public void Foo{ 
    public string Fizz{get;set;} 
    public BarEnum Bar{get;set;} 
} 

public enum BarEnum {One,Two,Three} 

var lst=new List<Foo>(); 
lst.Distinct(new LambdaComparer<Foo>(
    (x1,x2)=>x1.Fizz==x2.Fizz&& 
      x1.Bar==x2.Bar)); 

Można nawet owinąć go wokół, aby uniknąć pisania głośny new LambdaComparer<T>(...) rzecz:

public static class EnumerableExtensions{ 
public static IEnumerable<T> SmartDistinct<T> 
    (this IEnumerable<T> lst, Func<T, T, bool> pred){ 
    return lst.Distinct(new LambdaComparer<T>(pred)); 
} 
} 

Zastosowanie:

lst.SmartDistinct((x1,x2)=>x1.Fizz==x2.Fizz&&x1.Bar==x2.Bar); 

Uwaga: działa niezawodnie tylko dla Linq2Objects

1

nie robisz to źle, to jest po prostu złe wdrożenie .Distinct() w .NET Framework.

Jeden sposób naprawy jest już pokazany w innych odpowiedziach, ale dostępne jest również krótsze rozwiązanie, które ma tę zaletę, że można go używać jako metody rozszerzającej łatwo wszędzie bez konieczności modyfikowania skrótu obiektu wartości.

Spójrz na to:


Zastosowanie:

var myQuery=(from x in Customers select x).MyDistinct(d => d.CustomerID); 

Uwaga: W tym przykładzie użyto zapytania do bazy danych, ale ma także pracować z przeliczalnego wykazie obiektów.


Deklaracja MyDistinct:

public static class Extensions 
{ 
    public static IEnumerable<T> MyDistinct<T, V>(this IEnumerable<T> query, 
                Func<T, V> f) 
    { 
     return query.GroupBy(f).Select(x=>x.First()); 
    } 
} 

I działa na wszystko, obiekty, jak również podmiotów.W razie potrzeby możesz utworzyć drugą przeciążoną metodę rozszerzenia dla IQueryable<T>, zastępując typ zwracany i pierwszy typ parametru w przykładzie podanym powyżej.