Lo contrario de Intersect ()

276

Intersect se puede usar para encontrar coincidencias entre dos colecciones, así:

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

Sin embargo, lo que me gustaría lograr es lo contrario, me gustaría enumerar los elementos de una colección que faltan en la otra :

// 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
fuente
13
confirme si desea 4 como salida, o 1 y 4
Øyvind Bråthen
@ oyvind-knobloch-brathen Sí, solo me gustaría 4
Peter Bridger
23
Como nota al margen, este tipo de conjunto se denomina diferencia simétrica .
Mike T
19
Técnicamente hablando, una diferencia simétrica resultaría en [1, 4]. Dado que Peter solo quería los elementos en la matriz2 que no están en la matriz1 (es decir, 4), eso se llama un Complemento Relativo (también conocido como Diferencia teórica establecida)
rtorres

Respuestas:

377

Como se indicó, si desea obtener 4 como resultado, puede hacer lo siguiente:

var nonintersect = array2.Except(array1);

Si desea la no intersección real (también 1 y 4), entonces esto debería ser el truco:

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

Esta no será la solución más eficiente, pero para listas pequeñas debería funcionar bien.

Øyvind Bråthen
fuente
2
¿Cuál sería una solución de mejor rendimiento? ¡Gracias!
shanabus
66
Probablemente pueda hacerlo más rápido utilizando dos bucles for anidados, pero el código será mucho más sucio que esto. Contando la legibilidad en esto también, claramente usaría esta variante ya que es muy fácil de leer.
Øyvind Bråthen
55
Solo un punto de margen para agregar, si tiene: int [] antes = {1, 2, 3}; int [] después = {2, 3, 3, 4}; e intenta usar Except para encontrar lo que se ha agregado a 'after' desde 'before': var diff = after.Except (before); 'diff' contiene 4, no 3,4. es decir, ten cuidado con los elementos duplicados que te dan resultados inesperados
Paul Ryland
¿Funcionaría mejor? array1.AddRange (array2.Except (array1));
LBW
86

Puedes usar

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

O puedes usar

var difference = new HashSet(a);
difference.SymmetricExceptWith(b);
sehe
fuente
2
Uso interesante de SymmetricExceptWith (), no habría pensado en ese enfoque
Peter Bridger
SymmetricExceptWithEs probablemente mi método favorito.
Ash Clarke
66
Comparé los dos en una aplicación real donde tenía un par de listas de aproximadamente 125 cadenas en cada uno de ellos. El uso del primer enfoque es en realidad más rápido para listas de ese tamaño, aunque en su mayoría es insignificante ya que ambos enfoques tienen menos de medio milisegundo.
Dan
1
Sería bueno si el BCL tuviera un método de extensión de Linq para esto. Parece una omisión.
Drew Noakes
Alguien comparó SymmetricExceptWith y lo encontró mucho más rápido: skylark-software.com/2011/07/linq-and-set-notation.html
Colin
11

Este código enumera cada secuencia solo una vez y se usa Select(x => x)para ocultar el resultado para obtener un método de extensión de estilo Linq limpio. Ya que usa HashSet<T>su tiempo de ejecución es O(n + m)si los hashes están bien distribuidos. Se omiten elementos duplicados en cualquiera de las listas.

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
fuente
6

Creo que podrías estar buscando Except:

El operador Excepto produce la diferencia establecida entre dos secuencias. Solo devolverá elementos en la primera secuencia que no aparecen en la segunda. Opcionalmente, puede proporcionar su propia función de comparación de igualdad.

Consulte este enlace , este enlace o Google, para obtener más información.

Grant Thomas
fuente
2

No estoy 100% seguro de lo que se supone que debe hacer su método NonIntersect (con respecto a la teoría de conjuntos): ¿es
B \ A (todo lo de B que no ocurre en A)?
En caso afirmativo, debería poder utilizar la operación Excepto (B.Excepto (A)).

Frank Schmitt
fuente
Intersección de conjuntos == A∪B \ A∩B
amuliar el
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
fuente
2

array1.NonIntersect (array2);

No intersector dicho operador no está presente en Linq que debe hacer

excepto -> unión -> excepto

a.except(b).union(b.Except(a));
más seguro
fuente
-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
fuente