Il contrario di Intersect ()


276

Intersect può essere utilizzato per trovare corrispondenze tra due raccolte, in questo modo:

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

Tuttavia, ciò che vorrei ottenere è l'opposto, vorrei elencare gli elementi di una raccolta che mancano all'altra :

// 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
si prega di confermare se si desidera 4 come uscita o 1 e 4
Øyvind Bråthen

@ oyvind-knobloch-brathen Sì, vorrei solo 4
Peter Bridger,

23
Come nota a margine, questo tipo di set è chiamato Differenza simmetrica .
Mike T

19
Tecnicamente parlando, una differenza simmetrica si tradurrebbe in [1, 4]. Dal momento che Peter voleva solo gli elementi in array2 che non sono in array1 (ovvero, 4), questo è chiamato un complemento relativo (noto anche come differenza teorica set)
rorrida

Risposte:


377

Come detto, se vuoi ottenere 4 come risultato, puoi fare così:

var nonintersect = array2.Except(array1);

Se vuoi la vera non-intersezione (anche sia 1 che 4), allora questo dovrebbe fare il trucco:

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

Questa non sarà la soluzione più performante, ma per piccoli elenchi dovrebbe funzionare bene.


2
quale sarebbe una soluzione dalle prestazioni migliori? Grazie!
Shanabus,

6
Probabilmente puoi farlo più velocemente usando due cicli nidificati per, ma il codice sarà molto più sporco di così. Contando la leggibilità anche in questo, utilizzerei chiaramente questa variante in quanto è molto facile da leggere.
Øyvind Bråthen,

5
Solo un punto laterale da aggiungere, se si dispone di: int [] before = {1, 2, 3}; int [] after = {2, 3, 3, 4}; e si tenta di utilizzare Tranne per trovare ciò che è stato aggiunto a 'dopo' da 'prima': var diff = after.Except (before); 'diff' contiene 4, non 3,4. cioè fai attenzione agli elementi duplicati che ti danno risultati inaspettati
Paul Ryland

Funzionerebbe meglio? array1.AddRange (array2.Except (array1));
LBW,

86

Puoi usare

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

Oppure puoi usare

var difference = new HashSet(a);
difference.SymmetricExceptWith(b);

2
Uso interessante di SymmetricExceptWith (), non avrei pensato a questo approccio
Peter Bridger,

SymmetricExceptWithè probabilmente il mio metodo preferito.
Ash Clarke,

6
Ho confrontato i due in una vera applicazione in cui avevo un paio di liste di circa 125 stringhe in ognuna di esse. L'uso del primo approccio è in realtà più rapido per elenchi di quelle dimensioni, sebbene sia per lo più insignificante poiché entrambi gli approcci sono inferiori a mezzo millisecondo.
Dan

1
Sarebbe bello se il BCL avesse un metodo di estensione Linq per questo. Sembra un'omissione.
Drew Noakes,

Qualcuno ha confrontato SymmetricExceptWith e l'ha trovato molto più velocemente: skylark-software.com/2011/07/linq-and-set-notation.html
Colin

11

Questo codice enumera ogni sequenza una sola volta e utilizza Select(x => x)per nascondere il risultato per ottenere un metodo di estensione pulito in stile Linq. Dal momento che utilizza il HashSet<T>suo runtime è O(n + m)se gli hash sono ben distribuiti. Gli elementi duplicati in entrambi gli elenchi vengono omessi.

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

Penso che potresti essere alla ricerca di Except:

L'operatore Except produce la differenza impostata tra due sequenze. Restituirà solo elementi nella prima sequenza che non compaiono nella seconda. Puoi facoltativamente fornire la tua funzione di confronto sull'uguaglianza.

Dai un'occhiata a questo link , questo link o Google per ulteriori informazioni.


2

Non sono sicuro al 100% di cosa dovrebbe fare il tuo metodo NonIntersect (per quanto riguarda la teoria degli insiemi) - è
B \ A (tutto da B che non si verifica in A)?
Se sì, allora dovresti essere in grado di usare l'operazione Except (B.Except (A)).


Intersezione di insiemi == 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);
    }
}

2

array1.NonIntersect (array2);

Non interagire con tale operatore non è presente in Linq che dovresti fare

tranne -> unione -> tranne

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"
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.