Przeciwieństwo Intersect ()

275

Przecięcie można wykorzystać do znalezienia dopasowań między dwiema kolekcjami, np .:

// 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
}

Jednak to, co chciałbym osiągnąć, jest odwrotnie, chciałbym wymienić elementy z jednej kolekcji, których brakuje w drugiej :

// 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
}
Peter Bridger
źródło
13
potwierdź, jeśli chcesz 4 jako wynik, lub 1 i 4
Øyvind Bråthen
@ oyvind-knobloch-brathen Tak, chciałbym tylko 4
Peter Bridger
23
Na marginesie, ten typ zestawu nazywa się Różnicą Symetryczną .
Mike T
19
Technicznie rzecz biorąc, różnica symetryczna spowodowałaby [1, 4]. Ponieważ Peter chciał tylko tych elementów w tablicy 2, których nie ma w tablicy 1 (tj. 4), jest to tak zwane uzupełnienie względne (inaczej Różnica teoretyczna)
rtorres

Odpowiedzi:

376

Jak powiedziano, jeśli chcesz uzyskać 4, możesz zrobić w ten sposób:

var nonintersect = array2.Except(array1);

Jeśli chcesz prawdziwego braku przecięcia (również 1 i 4), to powinno załatwić sprawę:

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

Nie będzie to najbardziej wydajne rozwiązanie, ale w przypadku małych list powinno działać dobrze.

Øyvind Bråthen
źródło
2
jakie byłoby lepsze rozwiązanie? Dzięki!
shanabus
6
Prawdopodobnie możesz to zrobić szybciej, używając dwóch zagnieżdżonych pętli for, ale kod będzie znacznie brudniejszy niż ten. Licząc do tego również czytelność, wyraźnie użyłbym tego wariantu, ponieważ jest bardzo łatwy do odczytania.
Øyvind Bråthen
5
Tylko punkt boczny do dodania, jeśli masz: int [] before = {1, 2, 3}; int [] po = {2, 3, 3, 4}; i próbujesz użyć opcji Z wyjątkiem, aby znaleźć to, co zostało dodane do „po” od „przed”: var diff = after.Except (before); „diff” zawiera 4, a nie 3,4. tj. uważaj na duplikaty elementów dające nieoczekiwane wyniki
Paul Ryland
Czy to działałoby lepiej? array1.AddRange (array2.Except (array1));
LBW
86

Możesz użyć

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

Lub możesz użyć

var difference = new HashSet(a);
difference.SymmetricExceptWith(b);
sehe
źródło
2
Ciekawe użycie SymmetricExceptWith (), nie pomyślałbym o takim podejściu
Peter Bridger
SymmetricExceptWithjest prawdopodobnie moją ulubioną metodą.
Ash Clarke
6
Porównałem oba w prawdziwej aplikacji, w której miałem kilka list po około 125 ciągów w każdym z nich. Zastosowanie pierwszego podejścia jest w rzeczywistości szybsze w przypadku list o tej wielkości, choć jest ono w większości nieistotne, ponieważ oba podejścia są krótsze niż pół milisekundy.
Dan
1
Byłoby miło, gdyby BCL miał do tego metodę rozszerzenia Linq. To wydaje się pominięciem.
Drew Noakes
Ktoś porównał
Colin
11

Ten kod wylicza każdą sekwencję tylko raz i używa Select(x => x)do ukrycia wyniku, aby uzyskać czystą metodę rozszerzenia w stylu Linq. Ponieważ używa HashSet<T>środowiska O(n + m)uruchomieniowego, to jeśli skróty są dobrze rozłożone. Zduplikowane elementy na obu listach są pomijane.

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
źródło
6

Myślę, że możesz szukać Except:

Z wyjątkiem operatora tworzy ustawioną różnicę między dwiema sekwencjami. Zwróci tylko elementy w pierwszej sekwencji, które nie pojawiają się w drugiej. Opcjonalnie możesz podać własną funkcję porównywania równości.

Sprawdź ten link , ten link lub Google, aby uzyskać więcej informacji.

Grant Thomas
źródło
2

Nie jestem w 100% pewien, co powinna zrobić twoja metoda NonIntersect (w odniesieniu do teorii mnogości) - czy to
B \ A (wszystko z B, które nie występuje w A)?
Jeśli tak, powinieneś być w stanie skorzystać z operacji wyjątku (B.Except (A)).

Frank Schmitt
źródło
Przecięcie zbiorów == A∪B \ A∩B
osobliwy
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
źródło
2

array1.NonIntersect (array2);

Nie przecinaj takiego operatora nie ma w Linq, powinieneś to zrobić

oprócz -> związek -> oprócz

a.except(b).union(b.Except(a));
bezpieczniejszy
źródło
-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
źródło