2009-03-25 10 views
45

Diciamo che ho questi due array:Ottenere il "diff" tra due array in C#?

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

Vorrei ottenere le differenze tra i due. So che potrei scrivere questo in poche righe di codice, ma voglio assicurarmi che non manchi una funzionalità di lingua incorporata o un metodo di estensione LINQ.

Idealmente, avrei finito con i seguenti tre risultati:

  • I prodotti non in array1, ma sono in array2 ("D")
  • I prodotti non in array2, ma sono in array1 (" B ")
  • Gli elementi che sono in entrambi i

Grazie in anticipo!

risposta

86

Se disponi di LINQ, puoi utilizzare Except e Distinct. I set che hai chiesto per la questione sono rispettivamente:

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

Sai che tipo di garanzie sulle prestazioni sono una di queste?Presumibilmente Tranne dovrebbe prima fare una copia ordinata di ciascun array. Non riesco a trovare nulla di ciò su MSDN. – Eclipse

+1

No, non esegue una copia ordinata. Crea un set dalla sequenza esclusa e quindi scorre sulla sequenza sorgente, producendo qualsiasi elemento che non si trova nella sequenza esclusa. –

+3

(Quando dico "set" intendo "hash set".) –

10

dal 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

Ho dovuto fare cose simili a questo con molto grandi insiemi di dati. Se hai a che fare con qualche migliaio di persone, usa la roba di Linq poiché è molto più chiara. Ma se sai che i tuoi array sono pre-ordinati, l'esecuzione di un'unione come questa può farlo molto più velocemente, dato che fa un solo passaggio attraverso i dati e non ha bisogno di allocare tutta la memoria della versione 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++]); 
} 

Ancora, questo è davvero necessario solo se si hanno a che fare con centinaia di migliaia di valori.

3

Ecco i parametri di riferimento di metodi di estensione LINQ. I risultati sono stati ottenuti durante lo sviluppo di un vero programma.

Le prove: 2 liste (LST1 e LST2) ognuno di circa 250000 oggetti. Ogni oggetto (chiave di classe) contiene una stringa e un intero. Il secondo elenco contiene per lo più le stesse voci del primo, ma alcune nuove voci vengono aggiunte e alcune vengono rimosse.

Ho testato il Tranne metodo di estensione.

var eccetto = lst2.Except (LST1);

Lista LST = except.ToList();

Queste 2 linee prodotte 600 lista elementi di “novità”. L'ho cronometrato usando l'oggetto StopWatch. La velocità è sorprendente: 220 ms. Il computer che ho usato non è affatto un "Gonzales veloce". Core 2 Duo T7700 - 2,4 GHz.

Nota:

Ecco la chiave di classe, che implementa IEquatable i-faccia.

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