2011-06-08 62 views
23

我知道我可以調用linq的Except並指定一個自定義的IEqualityComparer,但是爲每個數據類型實現一個新的Comparer類看起來像是一個矯枉過正的目的。我可以使用lambda表達式來提供相等函數,就像我使用Where或其他LINQ函數時一樣?我可以在lambda表達式比較器中使用Linq的Except()嗎?

如果我不能,是否有其他選擇?

回答

7

我不認爲你可以直接使用基本的LINQ接口,但我看到人們用擴展方法實現了一個LambdaComparer類,它將幫助你做到這一點。

Here's an example

+0

是否有任何理由我需要阻止GetHash? – 2011-06-08 11:47:32

22

你能不能用。凡能過濾出你需要的值拉姆達?根據要求

例子:

static void Main(string[] args) 
    { 
     var firstCustomers = new[] { new Customer { Id = 1, Name = "Bob" }, new Customer { Id = 2, Name = "Steve" } }; 
     var secondCustomers = new[] { new Customer { Id = 2, Name = "Steve" }, new Customer { Id = 3, Name = "John" } }; 

     var customers = secondCustomers.Where(c => !firstCustomers.Select(fc => fc.Id).Contains(c.Id)); 
    } 

    public class Customer 
    { 
     public int Id { get; set; } 
     public string Name { get; set; } 
    } 
+0

我該怎麼辦?我想過濾出其ID不出現在另一個集合中的項目。 – 2011-06-08 11:15:06

+7

這種實現在計算上花費很大 - 對於第二客戶中的每個客戶,它都可能評估第一客戶中的每個客戶。由於複雜度爲O(n^2),當集合很大時,這會很明顯。這就是爲什麼LINQ中的Intersect運算符使用一個Set,它使用提供的相等比較器(使用GetHashCode)進行播種。 – 2014-08-06 00:33:29

4

下面是一些簡單的我掀起了:

public class CustomComparer<TSource, TCompareType> : IEqualityComparer<TSource> where TSource : class 
{ 
    private readonly Func<TSource, TCompareType> getComparisonObject; 
    public CustomComparer(Func<TSource,TCompareType> getComparisonObject) 
    { 
     if (getComparisonObject == null) throw new ArgumentNullException("getComparisonObject"); 
     this.getComparisonObject = getComparisonObject; 
    } 

    /// <summary> 
    /// Determines whether the specified objects are equal. 
    /// </summary> 
    /// <returns> 
    /// true if the specified objects are equal; otherwise, false. 
    /// </returns> 
    /// <param name="x">The first object of type <paramref name="T"/> to compare. 
    ///     </param><param name="y">The second object of type <paramref name="T"/> to compare. 
    ///     </param> 
    public bool Equals(TSource x, TSource y) 
    { 
     if (x == null) 
     { 
      return (y == null); 
     } 
     else if (y == null) 
     { 
      return false; 
     } 
     return EqualityComparer<TCompareType>.Default.Equals(getComparisonObject(x), getComparisonObject(y)); 
    } 

    /// <summary> 
    /// Returns a hash code for the specified object. 
    /// </summary> 
    /// <returns> 
    /// A hash code for the specified object. 
    /// </returns> 
    /// <param name="obj">The <see cref="T:System.Object"/> for which a hash code is to be returned. 
    ///     </param><exception cref="T:System.ArgumentNullException">The type of <paramref name="obj"/> is a reference type and <paramref name="obj"/> is null. 
    ///     </exception> 
    public int GetHashCode(TSource obj) 
    { 
     return EqualityComparer<TCompareType>.Default.GetHashCode(getComparisonObject(obj)); 
    } 
} 

用法:

var myItems = allItems.Except(theirItems, new CustomComparer(item => item.Name)); 
35

對於任何一個還在尋找;這是另一種實現自定義lambda比較器的方式。

public class LambdaComparer<T> : IEqualityComparer<T> 
    { 
     private readonly Func<T, T, bool> _expression; 

     public LambdaComparer(Func<T, T, bool> lambda) 
     { 
      _expression = lambda; 
     } 

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

     public int GetHashCode(T obj) 
     { 
      /* 
      If you just return 0 for the hash the Equals comparer will kick in. 
      The underlying evaluation checks the hash and then short circuits the evaluation if it is false. 
      Otherwise, it checks the Equals. If you force the hash to be true (by assuming 0 for both objects), 
      you will always fall through to the Equals check which is what we are always going for. 
      */ 
      return 0; 
     } 
    } 

您可以創建爲LINQ的擴展除了一個交集是採取拉姆達的

/// <summary> 
     /// Returns all items in the first collection except the ones in the second collection that match the lambda condition 
     /// </summary> 
     /// <typeparam name="T">The type</typeparam> 
     /// <param name="listA">The first list</param> 
     /// <param name="listB">The second list</param> 
     /// <param name="lambda">The filter expression</param> 
     /// <returns>The filtered list</returns> 
     public static IEnumerable<T> Except<T>(this IEnumerable<T> listA, IEnumerable<T> listB, Func<T, T, bool> lambda) 
     { 
      return listA.Except(listB, new LambdaComparer<T>(lambda)); 
     } 

     /// <summary> 
     /// Returns all items in the first collection that intersect the ones in the second collection that match the lambda condition 
     /// </summary> 
     /// <typeparam name="T">The type</typeparam> 
     /// <param name="listA">The first list</param> 
     /// <param name="listB">The second list</param> 
     /// <param name="lambda">The filter expression</param> 
     /// <returns>The filtered list</returns> 
     public static IEnumerable<T> Intersect<T>(this IEnumerable<T> listA, IEnumerable<T> listB, Func<T, T, bool> lambda) 
     { 
      return listA.Intersect(listB, new LambdaComparer<T>(lambda)); 
     } 

用法:

var availableItems = allItems.Except(filterItems, (p, p1) => p.Id== p1.Id); 
2

使用extension

public static IEnumerable<T> Except<T, TKey>(this IEnumerable<T> items, IEnumerable<T> other, 
                      Func<T, TKey> getKey) 
{ 
    return from item in items 
      join otherItem in other on getKey(item) 
      equals getKey(otherItem) into tempItems 
      from temp in tempItems.DefaultIfEmpty() 
      where ReferenceEquals(null, temp) || temp.Equals(default(T)) 
      select item; 

} 

Source

0

這裏是升,除了R溶液,基於一個LINQ外連接技術:

from l in new[] { 1, 2, 3 } 
join r in new[] { 2, 4, 5 } 
on l equals r 
into rr 
where !rr.Any() 
select l 

將產生:1,3

0

但你可以試試這個。這非常簡單,我認爲代碼包含錯誤。當然,代碼是少量的,沒有LINQ轉換爲db等

public static IEnumerable<TSource> ExceptPredicate<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, Func<TSource, TSource, bool> compare) { 
    foreach (var itmFirst in first) { 
    if (!second.Any(itmsecond => compare(itmFirst, itmsecond))) { 
     yield return itmFirst; 
    } 
    } 
    yield break; 
} 
相關問題