2010-07-15 28 views
2

我討厭張貼這個,因爲它有點主觀,但它感覺有一個更好的方法來做到這一點,我只是沒有想到。「更好」(更簡單,更快,無論)版本'不同的委託'?

有時候我想通過某些列/屬性來「區分」某個集合,但不會丟棄其他列(是的,這確實會丟失信息,因爲它會變成任意的那些其他列的值將隨之而來) 。

注意,這個擴充比拿,因爲這樣的事情的IEqualityComparer<T>可以做更復雜的比較邏輯含混重載那麼強大,但是這是我所需要的,現在:)

public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> getKeyFunc) 
{ 
    return from s in source 
      group s by getKeyFunc(s) into sourceGroups 
      select sourceGroups.First(); 
} 

用法示例:

var items = new[] 
{ 
    new { A = 1, B = "foo", C = Guid.NewGuid(), }, 
    new { A = 2, B = "foo", C = Guid.NewGuid(), }, 
    new { A = 1, B = "bar", C = Guid.NewGuid(), }, 
    new { A = 2, B = "bar", C = Guid.NewGuid(), }, 
}; 

var itemsByA = items.DistinctBy(item => item.A).ToList(); 
var itemsByB = items.DistinctBy(item => item.B).ToList(); 

回答

2

在這裏,你去。我不認爲這比你自己的版本更高效,但它應該有一個小小的優勢。它只需要對每個項目進行一次遍歷,而不需要首先對整個序列進行分組。

public static IEnumerable<TSource> DistinctBy<TSource, TKey>(
    this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) 
{ 
    return source.DistinctBy(keySelector, null); 
} 

public static IEnumerable<TSource> DistinctBy<TSource, TKey>(
    this IEnumerable<TSource> source, 
    Func<TSource, TKey> keySelector, IEqualityComparer<TKey> keyComparer) 
{ 
    if (source == null) 
     throw new ArgumentNullException("source"); 

    if (keySelector == null) 
     throw new ArgumentNullException("keySelector"); 

    return source.DistinctByIterator(keySelector, keyComparer); 
} 

private static IEnumerable<TSource> DistinctByIterator<TSource, TKey>(
    this IEnumerable<TSource> source, 
    Func<TSource, TKey> keySelector, IEqualityComparer<TKey> keyComparer) 
{ 
    var keys = new HashSet<TKey>(keyComparer); 

    foreach (TSource item in source) 
    { 
     if (keys.Add(keySelector(item))) 
      yield return item; 
    } 
} 
+0

+1這就是我要做的;) – 2010-07-15 22:14:22

2

我以前寫的通用Func => IEqualityComparer工具類,只是爲了能夠調用具有編寫自定義接受IEqualityComparer的LINQ方法重載的目的每次上課。

它使用委託(就像你的例子)來提供比較語義。這使我可以使用庫方法的內置實現,而不是滾動我自己的方法 - 我認爲這更有可能是正確和有效實施的。

public static class ComparerExt 
{ 
    private class GenericEqualityComparer<T> : IEqualityComparer<T> 
    { 
     private readonly Func<T, T, bool> m_CompareFunc; 

     public GenericEqualityComparer(Func<T,T,bool> compareFunc) { 
      m_CompareFunc = compareFunc; 
     } 

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

     public int GetHashCode(T obj) { 
      return obj.GetHashCode(); // don't override hashing semantics 
     } 
    } 

    public static IComparer<T> Compare<T>(Func<T,T,bool> compareFunc) { 
     return new GenericEqualityComparer<T>(compareFunc); 
    } 
} 

您可以使用此像這樣:

var result = list.Distinct(ComparerExt.Compare((a,b) => { /*whatever*/ }); 

我也經常扔在一個Reverse()方法允許改變操作數的排序中的比較,像這樣:

private class GenericComparer<T> : IComparer<T> 
{ 
    private readonly Func<T, T, int> m_CompareFunc; 
    public GenericComparer(Func<T,T,int> compareFunc) { 
     m_CompareFunc = compareFunc; 
    } 
    public int Compare(T x, T y) { 
     return m_CompareFunc(x, y); 
    } 
} 

public static IComparer<T> Reverse<T>(this IComparer<T> comparer) 
{ 
    return new GenericComparer<T>((a, b) => comparer.Compare(b, a)); 
} 
+0

鮮明需要的IEqualityComparer,而不是一個IComparer的...否則,這是一個很好的解決方案;) – 2010-07-15 20:53:38

+0

@Thomas - 你說得對。我沒有注意到我正在粘貼ComparerExt類的哪一部分(正如你可以想象的那樣,它有一堆其他有用的方法,我試圖爲了簡潔而排除)。我糾正了我的答案。 – LBushkin 2010-07-15 21:03:54

+0

你有可能發佈整個CompareExt類的地方嗎?聽起來非常有用:) – 2010-07-16 03:24:14

相關問題