I have two collections a
and b
. I would like to compute the set of items in either a
or b
, but not in both (a logical exc
We had a similar need for a project in my company, so we wrote this extension:
public class EnumerablePair : IReadOnlyCollection
{
private IReadOnlyCollection _Left;
private IReadOnlyCollection _Right;
private IEnumerable _Union;
private int _Count;
public EnumerablePair(IEnumerable left, IEnumerable right)
{
_Left = left?.ToList() ?? Enumerable.Empty().ToList();
_Right = right?.ToList() ?? Enumerable.Empty().ToList();
_Count = Left.Count + Right.Count;
_Union = Left.Union(Right);
}
public int Count => _Count;
public IReadOnlyCollection Left { get => _Left; }
public IReadOnlyCollection Right { get => _Right; }
public IEnumerator GetEnumerator()
{
return _Union.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _Union.GetEnumerator();
}
}
public static class EnumerableExtension
{
public static EnumerablePair ExclusiveDisjunction(this IEnumerable leftOperand, IEnumerable rightOperand, IEqualityComparer comparer = null)
{
if (leftOperand == null)
throw new ArgumentNullException(nameof(leftOperand), $"{nameof(leftOperand)} is null.");
if (rightOperand == null)
throw new ArgumentNullException(nameof(rightOperand), $"{nameof(rightOperand)} is null.");
// TODO : Can be optimized if one of the IEnumerable parameters is empty.
bool leftIsBigger = leftOperand.Count() > rightOperand.Count();
var biggestOperand = leftIsBigger ? leftOperand.ToList() : rightOperand.ToList();
var smallestOperand = leftIsBigger ? rightOperand.ToList() : leftOperand.ToList();
var except1 = biggestOperand.ToList();
var except2 = Enumerable.Empty().ToList();
Func areEquals;
if (comparer != null)
areEquals = (one, theOther) => comparer.Equals(one, theOther);
else
areEquals = (one, theOther) => one?.Equals(theOther) ?? theOther == null;
foreach (T t in smallestOperand)
if (except1.RemoveAll(item => areEquals(item, t)) == 0)
except2.Add(t);
if (leftIsBigger)
return new EnumerablePair(except1, except2);
return new EnumerablePair(except2, except1);
}
}
It compares elements of two collections (using an IEqualityComparer
or not, at your choice).
EnumerablePair
, contains objects that are in leftOperand
or rightOperand
, but not both (XOR).EnumerablePair.Left
contains objects that are in leftOperand
but not in rightOperand
.EnumerablePair.Right
contains objects that are in rightOperand
but not in leftOperand
.You can use the extension like this :
var xorList = list1.ExclusiveDisjunction(list2);
var leftXor = xorList.Left;
var rightXor = xorList.Right;
xorList
, leftXor
and rightXor
are IEnumerable
.