O oposto de Intersect ()

276

O Intersect pode ser usado para encontrar correspondências entre duas coleções, assim:

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

No entanto, o que eu gostaria de conseguir é o contrário, gostaria de listar itens de uma coleção que estão faltando na outra :

// 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
fonte
13
por favor confirme se você quiser 4 como a saída, ou 1 e 4
Øyvind Bråthen
@ oyvind-knobloch-brathen Sim, eu gostaria apenas 4 #
Peter Bridger
23
Como uma observação lateral, esse tipo de conjunto é chamado de Diferença simétrica .
Mike T
19
Tecnicamente falando, uma diferença simétrica resultaria em [1, 4]. Como Peter queria apenas os elementos do array2 que não estão no array1 (isto é, 4), isso se chama Complemento Relativo (também conhecido como Diferença Teórica dos Set)
rtorres

Respostas:

377

Como afirmado, se você deseja obter 4 como resultado, pode fazer o seguinte:

var nonintersect = array2.Except(array1);

Se você deseja a não interseção real (também 1 e 4), faça o seguinte:

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

Essa não será a solução com melhor desempenho, mas para listas pequenas ela deve funcionar muito bem.

Øyvind Bråthen
fonte
2
qual seria uma solução com melhor desempenho? Obrigado!
Shanabus
6
Provavelmente, você pode fazer isso mais rapidamente usando dois loops aninhados, mas o código será muito mais sujo do que isso. Contando também a legibilidade, eu usaria claramente essa variante, pois é muito fácil de ler.
Øyvind Bråthen
5
Apenas um ponto lateral a ser acrescentado, se você tiver: int [] before = {1, 2, 3}; int [] depois = {2, 3, 3, 4}; e você tenta usar Except para encontrar o que foi adicionado a 'after' since 'before': var diff = after.Except (before); 'diff' contém 4, não 3,4. ou seja, atente para elementos duplicados dando-lhe resultados inesperados
Paul Ryland
Isso teria um desempenho melhor? array1.AddRange (array2.Except (array1));
LBW
86

Você pode usar

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

Ou você pode usar

var difference = new HashSet(a);
difference.SymmetricExceptWith(b);
ver
fonte
2
Uso interessante de SymmetricExceptWith (), eu não teria pensado que a abordagem
Peter Bridger
SymmetricExceptWithprovavelmente é o meu método favorito.
quer
6
Comparei os dois em um aplicativo real, onde eu tinha algumas listas de cerca de 125 strings em cada um deles. Usar a primeira abordagem é realmente mais rápido para listas desse tamanho, embora seja principalmente insignificante, pois as duas abordagens estão abaixo de meio milissegundo.
Dan
1
Seria bom se o BCL tivesse um método de extensão Linq para isso. Parece uma omissão.
usar o seguinte
Alguém comparou o
Colin
11

Esse código enumera cada sequência apenas uma vez e usa Select(x => x)para ocultar o resultado para obter um método de extensão limpo no estilo Linq. Como ele usa HashSet<T>seu tempo de execução, éO(n + m) se os hashes estão bem distribuídos. Elementos duplicados em qualquer lista são omitidos.

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

Eu acho que você pode estar procurando Except:

O operador Except produz a diferença definida entre duas sequências. Ele retornará apenas elementos na primeira sequência que não aparecem na segunda. Opcionalmente, você pode fornecer sua própria função de comparação de igualdade.

Confira este link , este link ou o Google para obter mais informações.

Grant Thomas
fonte
2

Não tenho 100% de certeza do que seu método NonIntersect deve fazer (em relação à teoria dos conjuntos) - é
B \ A (tudo de B que não ocorre em A)?
Se sim, você poderá usar a operação Except (B.Except (A)).

Frank Schmitt
fonte
Interseção de conjuntos == A∪B \ A∩B
peculiar
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
fonte
2

array1.NonIntersect (array2);

Se um operador não digitalizado não estiver presente no Linq, você deve

exceto -> união -> exceto

a.except(b).union(b.Except(a));
mais seguro
fonte
-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
fonte