When I have 2 List<string>
objects, then I can use Intersect
and Except
on them directly to get an output IEnumerable<string>
. That's simple enough, but what if I want the intersection/disjuction on something more complex?
Example, trying to get a collection of ClassA
objects which is the result of the intersect on ClassA
object's AStr1
and ClassB
object's BStr
; :
public class ClassA {
public string AStr1 { get; set; }
public string AStr2 { get; set; }
public int AInt { get; set; }
}
public class ClassB {
public string BStr { get; set; }
public int BInt { get; set; }
}
public class Whatever {
public void xyz(List<ClassA> aObj, List<ClassB> bObj) {
// *** this line is horribly incorrect ***
IEnumberable<ClassA> result =
aObj.Intersect(bObj).Where(a, b => a.AStr1 == b.BStr);
}
}
How can I fix the noted line to achieve this intersection.
MoreLINQ has ExceptBy
. It doesn't have IntersectBy
yet, but you could easily write your own implementation, and possibly even contribute it to MoreLINQ afterwards :)
It would probably look something like this (omitting error checking):
public static IEnumerable<TSource> IntersectBy<TSource, TKey>(
this IEnumerable<TSource> first,
IEnumerable<TSource> second,
Func<TSource, TKey> keySelector,
IEqualityComparer<TKey> keyComparer)
{
HashSet<TKey> keys = new HashSet<TKey>(first.Select(keySelector),
keyComparer);
foreach (var element in second)
{
TKey key = keySelector(element);
// Remove the key so we only yield once
if (keys.Remove(key))
{
yield return element;
}
}
}
If you wanted to perform an intersection on two completely different types which happened to have a common property type, you could make a more general method with three type parameters (one for first
, one for second
, and one for the common key type).