I wrote a custom extension that returns the intersection between two collection and both the unique elements in a an b.
public static void CompareTo<T>(
this IEnumerable<T> sourceA,
IEnumerable<T> sourceB,
IEqualityComparer<T> equalityComparer,
out ICollection<T> commonElements,
out ICollection<T> elementsOnlyInSourceA,
out ICollection<T> elementsOnlyInSourceB)
where T : class
{
if (sourceA == null)
{
throw new ArgumentNullException(nameof(sourceA));
}
commonElements = new List<T>();
elementsOnlyInSourceA = new List<T>();
elementsOnlyInSourceB = new List<T>();
foreach (var elementA in sourceA)
{
var equalElementInSourceB = sourceB.FirstOrDefault(elementB => equalityComparer.Equals(elementB, elementA));
if (equalElementInSourceB != null)
{
commonElements.Add(elementA);
}
else
{
elementsOnlyInSourceA.Add(elementA);
}
}
elementsOnlyInSourceB = sourceB.Except(commonElements, equalityComparer).ToList();
}
I know I could just go ahead using Intersect
and Except
but this implementation should be a bit faster because I actually can determine both common and unique elements in sourceA
in a single iteration of sourceA
.
But thats not my point.
What kind of smells to me is that the caller cannot know which objects in commonElements
are actually beeing returned. Are they coming from sourceA
or sourceB
?
Even in Intersect
extension this is not made clear to me.
So what is it then?
How can I express that - in my case - I would always return common elements of sourceA
?
UPDATE: After considering comments/answers I refactored to this:
public static ICollection<T> Intersect<T>(
this IEnumerable<T> primarySource,
IEnumerable<T> secondarySource,
IEqualityComparer<T> equalityComparer,
out ICollection<T> uniqueInPrimarySource,
out ICollection<T> uniqueInSecondarySource)
where T : class
{
if (primarySource == null)
{
throw new ArgumentNullException(nameof(primarySource));
}
if (secondarySource == null)
{
throw new ArgumentNullException(nameof(secondarySource));
}
var commonElements = new List<T>();
uniqueInPrimarySource = new List<T>();
uniqueInSecondarySource = new List<T>();
var secondarySourceCopy = secondarySource.ToList();
foreach (var elementA in primarySource)
{
var equalElementInSourceB = secondarySourceCopy.FirstOrDefault(elementB => equalityComparer.Equals(elementB, elementA));
if (equalElementInSourceB != null)
{
commonElements.Add(elementA);
secondarySourceCopy.Remove(equalElementInSourceB);
}
else
{
uniqueInPrimarySource.Add(elementA);
}
}
uniqueInPrimarySource = secondarySourceCopy;
return commonElements;
}
I managed to get rid of a foreach by removing ab(intersect) elements from b.
UPDATE 2: Of couse this line
uniqueInPrimarySource = secondarySourceCopy;
should be
uniqueInSecondarySource = secondarySourceCopy;