Нахождение симметричного различия с LINQ

У меня есть два набора 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 в выводе.

20
задан PsylentKnight 25 May 2019 в 21:02
поделиться

2 ответа

Используйте 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))
    

Это все еще только две операции над множествами вместо трех в вашем оригинальном коде.

26
ответ дан 30 November 2019 в 00:31
поделиться

Учитывая, что a.Except(b) и b.Except(a) не совпадают, можно использовать concat вместо union, экономя оператор множества (а concat более эффективен).

return a.Except (b).Concat (b.Except (a));

При этом каждый список все равно обрабатывается дважды.

5
ответ дан 30 November 2019 в 00:31
поделиться
Другие вопросы по тегам:

Похожие вопросы: