У меня есть два набора a
и b
. Я хотел бы вычислить набор объектов в также a
или b
, но не в обоих (логическое эксклюзивное или). С LINQ я могу придумать это:
IEnumerable<T> Delta<T>(IEnumerable<T> a, IEnumerable<T> b)
{
return a.Except (b).Union (b.Except (a));
}
Интересно, существуют ли другие более эффективные или более компактные способы произвести различие между этими двумя наборами.
Редактирование 1: Jon Skeet отправил первое решение, которое не сохраняет порядок пунктов путем доверия a HashSet
. Интересно, существуют ли другие подходы, которые сохранили бы порядок a
и b
в выводе.
Используйте HashSet
напрямую - у него есть метод SymmetricExceptWith
:
HashSet<T> data = new HashSet<T>(a);
data.SymmetricExceptWith(b);
EDIT: Если вы хотите сохранить порядок, вот альтернатива:
HashSet<T> data = new HashSet<T>(a);
data.IntersectWith(b);
foreach (T t in a.Concat(b))
{
if (!data.Contains(t))
{
yield return t;
}
}
Здесь есть следующие важные отличия:
a
, и b
итерируются дважды. В некоторых случаях это может быть очень плохо - вы можете вызвать ToList
для каждого из них для начала, чтобы сохранить буфер. Если в a
или b
есть дубликаты, они будут возвращены несколько раз. Если вы хотите избежать этого, вы можете сохранить набор уже выданных значений. На данный момент это было бы эквивалентно:
a.Concat(b).Except(a.Intersect(b))
Это все еще только две операции над множествами вместо трех в вашем оригинальном коде.
Учитывая, что a.Except(b) и b.Except(a) не совпадают, можно использовать concat
вместо union
, экономя оператор множества (а concat
более эффективен).
return a.Except (b).Concat (b.Except (a));
При этом каждый список все равно обрабатывается дважды.