2011-02-04 24 views
19

La mia domanda è stato contrassegnato come un possibile duplicato di questa domanda: How to combine two dictionaries without looping?Combinare due Dizionari con LINQ

Credo che la mia domanda è diverso perché sto chiedendo come combinare due dizionari in un modo particolare: voglio tutti gli articoli da Dictionary1 più tutti gli elementi da Dictionary2 che non sono in (cioè la chiave non esiste) in Dictionary1.

ho due dizionari come questo:

var d1 = new Dictionary<string,object>(); 
var d2 = new Dictionary<string,object>(); 

d1["a"] = 1; 
d1["b"] = 2; 
d1["c"] = 3; 

d2["a"] = 11; 
d2["e"] = 12; 
d2["c"] = 13; 

vorrei combinarle in un nuovo dizionario (tecnicamente, non deve essere un dizionario, potrebbe essere solo una sequenza di KeyValuePairs) come che l'output contiene tutto il KeyValuePairs da d1 e solo KeyValuePairs da d2 la cui chiave non viene visualizzata in d1.

Concettualmente:

var d3 = d1.Concat(d2.Except(d1)) 

Ma che mi sta dando tutti gli elementi da D1 e D2.

Sembra che dovrebbe essere ovvio, ma mi manca qualcosa.

+0

Possibile duplicato [Come aggiungere 2 contenuti del dizionario senza eseguire il looping in C#] (http://stackoverflow.com/questions/712927/how-to-add-2-dicti onary-content-senza-looping-in-c-sharp) –

+1

Questa domanda non è un duplicato di quella domanda. Questa domanda sta chiedendo come combinare due dizionari, d1 e d2, in modo che il dizionario risultante abbia tutti gli elementi da d1 più tutti gli elementi di d2 che non sono già in d1. L'altra domanda è chiedere e le risposte spiegano come combinare due dizionari senza condizioni aggiuntive. – wageoghe

risposta

33

Quando si utilizza Except per impostazione predefinita, utilizza il confronto di uguaglianza predefinito, che per il tipo KeyValuePair confronta sia le chiavi che i valori. Si potrebbe invece questo approccio:

var d3 = d1.Concat(d2.Where(kvp => !d1.ContainsKey(kvp.Key))); 
1

È possibile anche utilizzare il proprio IEqualityComparer. Esempio di seguito:

public class MyComparer : IEqualityComparer<KeyValuePair<string,string>> { 
    public bool Equals(KeyValuePair<string, string> x, KeyValuePair<string, string> y) { 
     return x.Key.Equals(y.Key); 
    } 

    public int GetHashCode(KeyValuePair<string, string> obj) { 
     return obj.Key.GetHashCode(); 
    } 
} 

... 

Dictionary<string, string> d1 = new Dictionary<string, string>(); 
d1.Add("A", "B"); 
d1.Add("C", "D"); 

Dictionary<string, string> d2 = new Dictionary<string, string>(); 
d2.Add("E", "F"); 
d2.Add("A", "D"); 
d2.Add("G", "H"); 

MyComparer comparer = new MyComparer(); 

var d3 = d1.Concat(d2.Except(d1, comparer)); 
foreach (var a in d3) { 
    Console.WriteLine("{0}: {1}", a.Key, a.Value); 
} 
+0

Grazie! Questo è un approccio piuttosto interessante come quello di Mark. Ho visto che potevo usare un IEqualityComparer, ma mi chiedevo se esistesse un modo ancora più semplice. – wageoghe

+0

Normalmente per cose veloci come i confronti KEY, LINQ è probabilmente la strada da percorrere, ma se hai bisogno di qualcosa di un po 'più complicato (e devi riutilizzarlo), 'IEqualityComparer' potrebbe essere migliore, specialmente se implementato come metodo di estensione. – bitxwise

1

Jon Skeet (come al solito) ha un metodo di estensione che permette di fare questo: Can I specify my explicit type comparator inline?

+0

È davvero fantastico! Probabilmente non lo farò adesso, ma lo archivierò per dopo. – wageoghe

+0

Ci sono molti carichi e molte estensioni Linq. Avrei usato ExceptBy in questo caso, vieni a pensarci: http://code.google.com/p/morelinq/source/browse/trunk/MoreLinq/ExceptBy.cs – DaveShaw

7
var d3 = d1.Concat(d2.Where(kvp => ! d1.ContainsKey(kvp.Key))) 
      .ToDictionary(x => x.Key, x => x.Value); 

Questo sta lavorando per me.

1

Un'altra soluzione utilizzando il proprio IEqualityComparer come nella risposta di @bitxwise e @DaveShaw, ma non utilizzando Except() che lo rende un po 'più semplice:

var d3 = d1.Concat(d2).Distinct(new MyComparer()); 
2

Beh non so se si tratta di una nuova funzionalità in LINQ, ma questo è esattamente quello che fa .Union():

var d3 = d1.Union(d2); 

Naturalmente con Dizionari dovrete invia un confronto di uguaglianza personalizzato per soddisfare solo i tasti:

class KeyValuePairComparer<TKey, TValue> : IEqualityComparer<KeyValuePair<TKey, TValue>> 
{ 
    public bool Equals(KeyValuePair<TKey, TValue> x, KeyValuePair<TKey, TValue> y) 
    { 
     return x.Key.Equals(y.Key); 
    } 
    public int GetHashCode(KeyValuePair<TKey, TValue> x) 
    { 
     return x.GetHashCode(); 
    } 
} 

e poi:

var d3 = d1.Union(d2, new KeyValuePairComparer<string, object>()); 

Con il vostro esempio, l'uscita sarebbe (testato in C# interattivo):

> d1.Union(d2, new KeyValuePairComparer<string, object>()) 
UnionIterator { { "a", 1 }, { "b", 2 }, { "c", 3 }, { "e", 12 } } 

Nota la differenza:

> d2.Union(d1, new KeyValuePairComparer<string, object>()) 
UnionIterator { { "a", 11 }, { "e", 12 }, { "c", 13 }, { "b", 2 } }