Le contraire d'Intersect ()

276

Intersect peut être utilisé pour trouver des correspondances entre deux collections, comme ceci:

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

Cependant, ce que je voudrais réaliser est le contraire, je voudrais énumérer éléments d'une collection qui manquent dans l'autre :

// 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
la source
13
veuillez confirmer si vous voulez 4 comme sortie, ou 1 et 4
Øyvind Bråthen
@ oyvind-knobloch-brathen Oui, je ne voudrais que 4
Peter Bridger
23
En remarque, ce type d'ensemble est appelé une différence symétrique .
Mike T
19
Techniquement parlant, une différence symétrique se traduirait par [1, 4]. Puisque Peter ne voulait que les éléments de array2 qui ne sont pas dans array1 (c'est-à-dire 4), cela s'appelle un complément relatif (alias Set-Theoretic Difference)
rtorres

Réponses:

377

Comme indiqué, si vous souhaitez obtenir 4 comme résultat, vous pouvez faire comme ceci:

var nonintersect = array2.Except(array1);

Si vous voulez la véritable non-intersection (également à la fois 1 et 4), cela devrait faire l'affaire:

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

Ce ne sera pas la solution la plus performante, mais pour les petites listes, cela devrait très bien fonctionner.

Øyvind Bråthen
la source
2
quelle serait une solution plus performante? Merci!
shanabus
6
Vous pouvez probablement le faire plus rapidement en utilisant deux boucles imbriquées pour, mais le code sera beaucoup plus sale que cela. En comptant également la lisibilité, j'utiliserais clairement cette variante car elle est très facile à lire.
Øyvind Bråthen
5
Juste un point secondaire à ajouter, si vous avez: int [] avant = {1, 2, 3}; int [] après = {2, 3, 3, 4}; et vous essayez d'utiliser Except pour trouver ce qui a été ajouté à «après» depuis «avant»: var diff = after.Except (avant); 'diff' contient 4, pas 3,4. c'est à dire faites attention aux éléments en double qui vous donnent des résultats inattendus
Paul Ryland
Cela fonctionnerait-il mieux? array1.AddRange (array2.Except (array1));
LBW
86

Vous pouvez utiliser

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

Ou vous pouvez utiliser

var difference = new HashSet(a);
difference.SymmetricExceptWith(b);
sehe
la source
2
Utilisation intéressante de SymmetricExceptWith (), je n'aurais pas pensé à cette approche
Peter Bridger
SymmetricExceptWithest probablement ma méthode préférée.
Ash Clarke du
6
J'ai comparé les deux dans une application réelle où j'avais quelques listes d'environ 125 chaînes dans chacune d'elles. L'utilisation de la première approche est en fait plus rapide pour les listes de cette taille, bien que ce soit généralement insignifiant car les deux approches étaient inférieures à une demi-milliseconde.
Dan
1
Ce serait bien si la BCL avait une méthode d'extension Linq pour cela. Cela semble être une omission.
Drew Noakes
Quelqu'un a comparé SymmetricExceptWith et l'a trouvé beaucoup plus rapidement: skylark-software.com/2011/07/linq-and-set-notation.html
Colin
11

Ce code énumère chaque séquence une seule fois et permet Select(x => x)de masquer le résultat pour obtenir une méthode d'extension de style Linq propre. Puisqu'il utilise HashSet<T>son runtime, c'est O(n + m)si les hachages sont bien distribués. Les éléments en double dans l'une ou l'autre liste sont omis.

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
la source
6

Je pense que vous cherchez peut-être Except:

L'opérateur Except produit la différence définie entre deux séquences. Il ne renverra que les éléments de la première séquence qui n'apparaissent pas dans la seconde. Vous pouvez éventuellement fournir votre propre fonction de comparaison d'égalité.

Consultez ce lien , ce lien ou Google pour plus d'informations.

Grant Thomas
la source
2

Je ne suis pas sûr à 100% de ce que votre méthode NonIntersect est censée faire (en ce qui concerne la théorie des ensembles) - est-ce
B \ A (tout ce qui provient de B ne se produit pas dans A)?
Si oui, vous devriez pouvoir utiliser l'opération Except (B.Except (A)).

Frank Schmitt
la source
Intersection d'ensembles == 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
la source
2

array1.NonIntersect (array2);

Un tel opérateur sans intersection n'est pas présent dans Linq, vous devriez le faire

sauf -> union -> sauf

a.except(b).union(b.Except(a));
safder
la source
-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
la source