Created
April 4, 2019 07:28
-
-
Save mladenb/b76bcbc4063f138289243fb06d099dda to your computer and use it in GitHub Desktop.
.NET Core ExceptBy / IntersectBy
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static IEnumerable<TFirst> ExceptBy<TFirst, TSecond>(this IEnumerable<TFirst> first, Func<TFirst, TSecond> mappingFunc, IEnumerable<TSecond> second) | |
{ | |
if (first == null) throw new ArgumentNullException(nameof(first)); | |
if (second == null) throw new ArgumentNullException(nameof(second)); | |
return ExceptByIterator(first, mappingFunc, second, null); | |
} | |
public static IEnumerable<TFirst> ExceptBy<TFirst, TSecond>(this IEnumerable<TFirst> first, Func<TFirst, TSecond> mappingFunc, IEnumerable<TSecond> second, IEqualityComparer<TSecond> comparer) | |
{ | |
if (first == null) throw new ArgumentNullException(nameof(first)); | |
if (second == null) throw new ArgumentNullException(nameof(second)); | |
return ExceptByIterator(first, mappingFunc, second, comparer); | |
} | |
private static IEnumerable<TFirst> ExceptByIterator<TFirst, TSecond>(IEnumerable<TFirst> first, Func<TFirst, TSecond> mappingFunc, IEnumerable<TSecond> second, IEqualityComparer<TSecond> comparer) | |
{ | |
var set = new HashSet<TSecond>(comparer); | |
foreach (var element in second) | |
{ | |
set.Add(element); | |
} | |
foreach (var element in first) | |
{ | |
if (!set.Contains(mappingFunc(element))) yield return element; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static IEnumerable<TFirst> IntersectBy<TFirst, TSecond>(this IEnumerable<TFirst> first, Func<TFirst, TSecond> mappingFunc, IEnumerable<TSecond> second) | |
{ | |
if (first == null) throw new ArgumentNullException(nameof(first)); | |
if (second == null) throw new ArgumentNullException(nameof(second)); | |
return IntersectByIterator(first, mappingFunc, second, null); | |
} | |
public static IEnumerable<TFirst> IntersectBy<TFirst, TSecond>(this IEnumerable<TFirst> first, Func<TFirst, TSecond> mappingFunc, IEnumerable<TSecond> second, IEqualityComparer<TSecond> comparer) | |
{ | |
if (first == null) throw new ArgumentNullException(nameof(first)); | |
if (second == null) throw new ArgumentNullException(nameof(second)); | |
return IntersectByIterator(first, mappingFunc, second, comparer); | |
} | |
private static IEnumerable<TFirst> IntersectByIterator<TFirst, TSecond>(IEnumerable<TFirst> first, Func<TFirst, TSecond> mappingFunc, IEnumerable<TSecond> second, IEqualityComparer<TSecond> comparer) | |
{ | |
var set = new HashSet<TSecond>(comparer); | |
foreach (var element in second) | |
{ | |
set.Add(element); | |
} | |
foreach (var firstItem in first) | |
{ | |
if (set.Contains(mappingFunc(firstItem))) yield return firstItem; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The original Except/Intersect return a collection of unique items, even though their contract doesn't state so (e.g. the return value of those methods isn't a HashSet/Set, but rather IEnumerable), which is probably a result of a poor design decision. Instead, we can use more intuitive implementation, which returns as much of the same elements from the first enumeration as there are, not just a unique one (using Set.Contains).
Further more, mapping function was added in order to help intersect/except collections of different types.
Original source code:
https://github.com/dotnet/corefx/blob/master/src/System.Linq/src/System/Linq/Except.cs
https://github.com/dotnet/corefx/blob/master/src/System.Linq/src/System/Linq/Intersect.cs