2012-04-17 270 views
2

我有一個C#中的對象列表。所有的對象都包含屬性dept和course。
有幾個對象具有相同的部門和課程。從列表中刪除具有重複屬性的對象

如何修剪列表(或創建新列表),其中每個唯一的對象(部分&當然)屬性只有一個對象。

[任何額外的副本都跌出名單]

我知道如何與一個單一的屬性來完成這個:

fooList.GroupBy(x => x.dept).Select(x => x.First()); 

不過,我想知道如何爲多個屬性做到這一點( 2或更多)?

+0

請問您的列表需要可排序? – 2012-04-17 13:39:15

+0

我到達這一點時已經對列表進行了排序。 – Baxter 2012-04-17 13:41:54

回答

5

要使用多個屬性,你可以使用匿名類型:

var query = fooList.GroupBy(x => new { x.Dept, x.Course }) 
        .Select(x => x.First()); 

當然,這要看是什麼類型的DeptCourse是確定的平等。或者,您的課程可以實施IEqualityComparer<T>,然後您可以使用接受比較器的Enumerable.Distinct method

+0

部門和課程均爲整數。 – Baxter 2012-04-17 13:44:40

+0

@Baxter然後這種方法將工作得很好。 – 2012-04-17 13:53:05

+0

這似乎是伎倆!然而,我想知道,因爲我使用的是匿名類型,我怎樣才能傳遞「var query」? 什麼樣的方法簽名可以接受?還是有某種轉換我會返回到它的原始類型等? – Baxter 2012-04-17 19:06:13

2

另一種方法是用IEqualityComparer<Foo>一起使用LINQ Distinct擴展方法。它要求你實現一個比較器;然而,後者是可重用和可測試的。

public class FooDeptCourseEqualityComparer : IEqualityComparer<Foo> 
{ 
    public bool Equals(Foo x, Foo y) 
    { 
     return 
      x.Dept == y.Dept && 
      x.Course.ToLower() == y.Course.ToLower(); 
    } 

    public int GetHashCode(Foo obj) 
    { 
     unchecked { 
      return 527 + obj.Dept.GetHashCode() * 31 + obj.Course.GetHashCode(); 
     } 
    } 

    #region Singleton Pattern 

    public static readonly FooDeptCourseEqualityComparer Instance = 
     new FooDeptCourseEqualityComparer(); 

    private FooDeptCourseEqualityComparer() { } 

    #endregion 
} 

我的例子使用單例模式。由於該類沒有任何狀態信息,因此每次我們使用它時都不需要創建新的實例。

我的代碼不處理null值。當然,如果可能發生,你將不得不處理它們。

獨特的值返回這樣

var result = fooList.Distinct(FooDeptCourseEqualityComparer.Instance); 

UPDATE

我建議使用接受lambda表達式在構造函數中,可以在多個場合重複使用一個通用的EqualityComparer類

public class LambdaEqualityComparer<T> : IEqualityComparer<T> 
{ 
    private Func<T, T, bool> _areEqual; 
    private Func<T, int> _getHashCode; 

    public LambdaEqualityComparer(Func<T, T, bool> areEqual, 
            Func<T, int> getHashCode) 
    { 
     _areEqual = areEqual; 
     _getHashCode = getHashCode; 
    } 

    public LambdaEqualityComparer(Func<T, T, bool> areEqual) 
     : this(areEqual, obj => obj.GetHashCode()) 
    { 
    } 

    #region IEqualityComparer<T> Members 

    public bool Equals(T x, T y) 
    { 
     return _areEqual(x, y); 
    } 

    public int GetHashCode(T obj) 
    { 
     return _getHashCode(obj); 
    } 

    #endregion 
} 

您可以使用它像這樣

var comparer = new LambdaEqualityComparer<Foo>(
    (x, y) => x.Dept == y.Dept && x.Course == y.Course, 
    obj => { 
     unchecked { 
      return 527 + obj.Dept.GetHashCode() * 31 + obj.Course.GetHashCode(); 
     } 
    } 
); 

var result = fooList.Distinct(comparer); 

注意:必須提供的哈希碼的計算,因爲Distinct使用內部Set<T>類,後者又使用散列碼。


更新#2

甚至更​​通用的相等比較器自動執行所述比較和接受屬性訪問的列表;但是,您無法控制比較的執行方式。

public class AutoEqualityComparer<T> : IEqualityComparer<T> 
{ 
    private Func<T, object>[] _propertyAccessors; 

    public AutoEqualityComparer(params Func<T, object>[] propertyAccessors) 
    { 
     _propertyAccessors = propertyAccessors; 
    } 

    #region IEqualityComparer<T> Members 

    public bool Equals(T x, T y) 
    { 
     foreach (var getProp in _propertyAccessors) { 
      if (!getProp(x).Equals(getProp(y))) { 
       return false; 
      } 
     } 
     return true; 
    } 

    public int GetHashCode(T obj) 
    { 
     unchecked { 
      int hash = 17; 
      foreach (var getProp in _propertyAccessors) { 
       hash = hash * 31 + getProp(obj).GetHashCode(); 
      } 
      return hash; 
     } 
    } 

    #endregion 
} 

使用

var comparer = new AutoEqualityComparer<Foo>(foo => foo.Dept, 
              foo => foo.Course); 
var result = fooList.Distinct(comparer); 
相關問題