2009-03-25 13 views
45

Digamos que tengo estas dos matrices:Obteniendo la "diferencia" entre dos matrices en C#?

var array1 = new[] {"A", "B", "C"}; 
var array2 = new[] {"A", "C", "D"}; 

me gustaría obtener las diferencias entre los dos. Sé que podría escribir esto en unas pocas líneas de código, pero quiero asegurarme de que no me falta una función de lenguaje integrado o un método de extensión LINQ.

Idealmente, me gustaría terminar con los siguientes tres resultados:

  • elementos no en array1, pero están en matriz2 ("D")
  • elementos no en matriz2, pero están en array1 (" B ")
  • Los productos que se encuentran en ambos

Gracias de antemano!

Respuesta

86

Si tiene LINQ disponible para usted, puede usar Except y Distinct. Los conjuntos que solicitó en la pregunta son, respectivamente:

- array2.Except(array1) 
- array1.Except(array2) 
- array1.Intersect(array2) 
+0

¿Sabes qué tipo de garantía de rendimiento es esta?Presumiblemente Except tendría que hacer primero una copia ordenada de cada matriz. No puedo encontrar nada de esto en MSDN. – Eclipse

+1

No, no hace una copia ordenada. Crea un conjunto a partir de la secuencia excluida, y luego itera sobre la secuencia fuente, produciendo cualquier elemento que no esté en la secuencia excluida. –

+3

(Cuando digo "set" me refiero a "hash set".) –

10

del MSDN 101 LINQ samples ....

public void Linq52() { 
    int[] numbersA = { 0, 2, 4, 5, 6, 8, 9 }; 
    int[] numbersB = { 1, 3, 5, 7, 8 }; 

    IEnumerable<int> aOnlyNumbers = numbersA.Except(numbersB); 

    Console.WriteLine("Numbers in first array but not second array:"); 
    foreach (var n in aOnlyNumbers) { 
     Console.WriteLine(n); 
    } 
} 
3

que he tenido que hacer cosas similares a esto con grandes conjuntos de datos. Si está lidiando con algunos miles más o menos, use las cosas de Linq, ya que es mucho más claro. Pero si sabe que sus matrices están previamente ordenadas, ejecutar una fusión como esta puede hacerlo significativamente más rápido, ya que solo hace que una pase a través de los datos y no necesita asignar tanta memoria como la versión de Linq.

int iA = 0; 
int iB = 0; 
List<int> inA = new List<int>(); 
List<int> inB = new List<int>(); 
List<int> inBoth = new List<int>(); 
while (iA < numbersA.Length && iB < numbersB.Length) 
{ 
    if (numbersA[iA] < numbersB[iB]) 
    { 
     inA.Add(numbersA[iA++]); 
    } 
    else if (numbersA[iA] == numbersB[iB]) 
    { 
     inBoth.Add(numbersA[iA++]); 
     ++iB; 
    } 
    else 
    { 
     inB.Add(numbersB[iB++]); 
    } 
} 
while (iA < numbersA.Length) 
{ 
    inA.Add(numbersA[iA++]); 
} 
while (iB < numbersB.Length) 
{ 
    inB.Add(numbersB[iB++]); 
} 

Nuevamente, esto solo es necesario si se trata de cientos de miles de valores.

3

Aquí están los puntos de referencia de los métodos de extensión LINQ. Los resultados se obtuvieron durante el desarrollo de un programa real.

Las pruebas: 2 listas (lst1 y lst2) cada uno aproximadamente 250000 objetos. Cada objeto (clave de clase) contiene una cadena y un número entero. La segunda lista contiene principalmente las mismas entradas que la primera, pero se agregan algunas entradas nuevas y algunas se eliminan.

He probado el método de extensión Except.

var except = lst2.Except (lst1);

Lista lst = except.ToList();

Estas 2 líneas produjeron 600 elementos de la lista de "nuevas incorporaciones". Lo sincronicé usando el objeto StopWatch. La velocidad es sorprendente: 220 ms. La computadora que utilicé no es de ninguna manera un "Gonzales rápido". Core 2 Duo T7700 - 2.4GHz.

Nota:

Aquí está la clave de clase, que implementa IEquatable i-cara.

public class Key : IEquatable<Key> 
{ 
    public int Index { get; private set; } 
    public string Name { get; private set; } 

    public Key(string keyName, int sdIndex) 
    { 
     this.Name = keyName; 
     this.Index = sdIndex; 
    } 

// IEquatable implementation 
    public bool Equals(Key other) 
    { 
     //Check whether the compared object is null. 
     if (Object.ReferenceEquals(other, null)) return false; 
     //Check whether the compared object references the same data. 
     if (Object.ReferenceEquals(this, other)) return true; 
     //Check whether the products' properties are equal. 
     return Index.Equals(other.Index) && Name.Equals(other.Name); 
    } 

    // If Equals() returns true for a pair of objects 
    // then GetHashCode() must return the same value for these objects. 
    public override int GetHashCode() 
    { 
     //Get hash code for the name field if it is not null. 
     int hashKeyName = Name == null ? 0 : Name.GetHashCode(); 
     //Get hash code for the index field. 
     int hashKeyIndex = Index.GetHashCode(); 
     //Calculate the hash code for the Key. 
     return hashKeyName^hashKeyIndex; 
    } 
} 
Cuestiones relacionadas