我可以使用Linq';s带有lambda表达式比较器的Except()
本文关键字:比较器 表达式 lambda Except 带有 可以使 Linq 我可以 | 更新日期: 2023-09-27 17:58:51
我知道我可以调用linq的Except并指定一个自定义的IEqualityComparer,但为每个数据类型实现一个新的Comparer类似乎有点过头了。我可以使用lambda表达式来提供相等函数吗,就像我使用Where或其他LINQ函数一样?
如果我做不到,还有别的选择吗?
对于任何仍在寻找的人;这里是实现自定义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创建一个扩展,除了接收lambda的的Intersect
/// <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);
你能不使用一个过滤掉所需值的lambda在哪里吗?
请求的示例:
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; }
}
我不认为你可以直接使用基本的LINQ接口,但我看到人们用扩展方法实现了LambdaComparer类,这将帮助你做到这一点。
以下是
我想出了一个简单的方法:
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));
使用扩展!
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;
}
源
这里是l,除了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
但您可以尝试一下。这太容易了,我认为代码中包含错误。当然,该代码是为少量,没有LINQ翻译成数据库等
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;
}