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
}

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.


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);

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


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);
}

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.


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)).


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);
    }
}

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));

-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"
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.