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
}

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.


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

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

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.


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


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

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

-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"
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.