Противоположность Intersect ()

276

Пересечение может использоваться для поиска совпадений между двумя коллекциями, например так:

// Assign two arrays.
int[] array1 = { 1, 2, 3 };
int[] array2 = { 2, 3, 4 };
// Call Intersect extension method.
var intersect = array1.Intersect(array2);
// Write intersection to screen.
foreach (int value in intersect)
{
    Console.WriteLine(value); // Output: 2, 3
}

Однако то, что я хотел бы достичь, это наоборот, я хотел бы перечислить элементы из одной коллекции, которые отсутствуют в другой :

// Assign two arrays.
int[] array1 = { 1, 2, 3 };
int[] array2 = { 2, 3, 4 };
// Call "NonIntersect" extension method.
var intersect = array1.NonIntersect(array2); // I've made up the NonIntersect method
// Write intersection to screen.
foreach (int value in intersect)
{
    Console.WriteLine(value); // Output: 4
}
Питер Бриджер
источник
13
Пожалуйста, подтвердите, если вы хотите 4 в качестве выхода, или 1 и 4
Ойвинд Bråthen
@ oyvind-knobloch-brathen Да, мне бы хотелось только 4
Питер Бриджер
23
В качестве примечания, этот тип набора называется симметричной разностью .
Майк Т
19
Технически говоря, симметричная разность приведет к [1, 4]. Так как Питер хотел только элементы в массиве 2, которых нет в массиве 1 (то есть 4), это называется относительным дополнением (он же Теоретико-множественное различие)
rtorres

Ответы:

377

Как уже говорилось, если вы хотите получить 4 в результате, вы можете сделать так:

var nonintersect = array2.Except(array1);

Если вы хотите реальное отсутствие пересечения (также и 1 и 4), то это должно сработать:

var nonintersect = array1.Except(array2).Union( array2.Except(array1));

Это не будет наиболее эффективным решением, но для небольших списков должно работать просто отлично.

Ойвинд Бротен
источник
2
что будет лучшим решением? Спасибо!
Шанабус
6
Вероятно, вы можете сделать это быстрее, используя два вложенных цикла for, но код будет намного грязнее, чем этот. Считая читабельность в этом, я бы четко использовал этот вариант, так как он очень легко читается.
Ойвинд Бротен
5
Просто боковую точку для добавления, если у вас есть: int [] before = {1, 2, 3}; int [] после = {2, 3, 3, 4}; и вы пытаетесь использовать Except, чтобы найти то, что было добавлено в 'after' since 'before': var diff = after.Except (before); 'diff' содержит 4, а не 3,4. т.е. остерегайтесь дублирующих элементов, дающих неожиданные результаты
Пол Риланд
Будет ли это работать лучше? array1.AddRange (array2.Except (массив1));
LBW
86

Ты можешь использовать

a.Except(b).Union(b.Except(a));

Или вы можете использовать

var difference = new HashSet(a);
difference.SymmetricExceptWith(b);
sehe
источник
2
Интересное использование SymmetricExceptWith (), я бы не подумал об этом подходе
Питер Бриджер
SymmetricExceptWithнаверное мой любимый метод.
Эш Кларк
6
Я сравнил их в реальном приложении, где у меня было несколько списков по 125 строк в каждой. Использование первого подхода на самом деле быстрее для списков такого размера, хотя, как правило, оно незначительно, так как оба подхода менее половины миллисекунды.
Дан
1
Было бы неплохо, если бы у BCL был метод расширения Linq для этого. Это похоже на упущение.
Дрю Ноакс
Кто-то протестировал SymmetricExceptWith и нашел его намного быстрее: skylark-software.com/2011/07/linq-and-set-notation.html
Colin
11

Этот код перечисляет каждую последовательность только один раз и использует Select(x => x) чтобы скрыть результат, чтобы получить чистый метод расширения в стиле Linq. Поскольку он использует HashSet<T>свое время выполнения, это O(n + m)если хеши хорошо распределены. Дублирующиеся элементы в любом списке опущены.

public static IEnumerable<T> SymmetricExcept<T>(this IEnumerable<T> seq1,
    IEnumerable<T> seq2)
{
    HashSet<T> hashSet = new HashSet<T>(seq1);
    hashSet.SymmetricExceptWith(seq2);
    return hashSet.Select(x => x);
}
CodesInChaos
источник
6

Я думаю, что вы можете искать Except:

Оператор Except создает разность множеств между двумя последовательностями. Он вернет только элементы в первой последовательности, которые не появляются во второй. При желании вы можете предоставить свою собственную функцию сравнения равенства.

Проверьте эту ссылку , эту ссылку или Google, для получения дополнительной информации.

Грант Томас
источник
2

Я не уверен на 100%, что должен делать ваш метод NonIntersect (в отношении теории множеств) - это
B \ A (все из B, чего нет в A)?
Если да, то вы сможете использовать операцию «Кроме» (B.Except (A)).

Фрэнк Шмитт
источник
Пересечение множеств == A∪B \ A∩B
amuliar
2
/// <summary>
/// Given two list, compare and extract differences
/// http://stackoverflow.com/questions/5620266/the-opposite-of-intersect
/// </summary>
public class CompareList
{
    /// <summary>
    /// Returns list of items that are in initial but not in final list.
    /// </summary>
    /// <param name="listA"></param>
    /// <param name="listB"></param>
    /// <returns></returns>
    public static IEnumerable<string> NonIntersect(
        List<string> initial, List<string> final)
    {
        //subtracts the content of initial from final
        //assumes that final.length < initial.length
        return initial.Except(final);
    }

    /// <summary>
    /// Returns the symmetric difference between the two list.
    /// http://en.wikipedia.org/wiki/Symmetric_difference
    /// </summary>
    /// <param name="initial"></param>
    /// <param name="final"></param>
    /// <returns></returns>
    public static IEnumerable<string> SymmetricDifference(
        List<string> initial, List<string> final)
    {
        IEnumerable<string> setA = NonIntersect(final, initial);
        IEnumerable<string> setB = NonIntersect(initial, final);
        // sum and return the two set.
        return setA.Concat(setB);
    }
}
Alcedo
источник
2

array1.NonIntersect (массив2);

Непересекающегося такого оператора нет в Linq, вы должны сделать

кроме -> союз -> кроме

a.except(b).union(b.Except(a));
safder
источник
-1
string left = "411329_SOFT_MAC_GREEN";
string right= "SOFT_MAC_GREEN";

string[] l = left.Split('_');
string[] r = right.Split('_');

string[] distinctLeft = l.Distinct().ToArray();
string[] distinctRight = r.Distinct().ToArray();

var commonWord = l.Except(r, StringComparer.OrdinalIgnoreCase)
string result = String.Join("_",commonWord);
result = "411329"
kiflay
источник