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
}

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.


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

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

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.


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


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

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

-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"
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.