2011-02-04 17 views
19

Mi pregunta se ha marcado como un posible duplicado de esta pregunta: How to combine two dictionaries without looping?Combinar dos diccionarios con LINQ

Creo que mi pregunta es diferente porque yo estoy preguntando cómo combinar dos diccionarios de una manera particular: Quiero que todos los artículos de Dictionary1 más todos los elementos de Dictionary2 que no están en (es decir, la clave no existe) en Dictionary1.

que tienen dos diccionarios de este tipo:

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; 

me gustaría combinarlos en un nuevo diccionario (técnicamente, no tiene por qué ser un diccionario, podría ser sólo una secuencia de KeyValuePairs) tales que la salida contiene todos los KeyValuePairs de d1 y solo los KeyValuePairs de d2 cuya clave no aparece en d1.

Conceptualmente:

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

Pero eso me está dando todos los elementos de D1 y D2.

Parece que debería ser obvio, pero me falta algo.

+0

duplicado posible de [Cómo agregar 2 contenidos del diccionario sin bucle en C#] (http://stackoverflow.com/questions/712927/how-to-add-2-dicti onary-contents-without-looping-in-c-sharp) –

+1

Esta pregunta no es un duplicado de esa pregunta. Esta pregunta es cómo combinar dos diccionarios, d1 y d2, de modo que el diccionario resultante tenga todos los elementos de d1 más todos los elementos de d2 que aún no estén en d1. La otra pregunta es preguntar y las respuestas explican cómo combinar dos diccionarios sin condiciones adicionales. – wageoghe

Respuesta

33

Cuando usa Except de forma predeterminada usa el comparador de igualdad predeterminado, que para el tipo KeyValuePair compara las claves y los valores. Se podría este enfoque en su lugar:

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

También puede utilizar su propia IEqualityComparer. Ejemplo a continuación:

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

Gracias! Ese es un enfoque genial además de Mark. Vi que podía usar un IEqualityComparer, pero me preguntaba si había una manera aún más fácil. – wageoghe

+0

Normalmente para cosas rápidas como comparaciones KEY, LINQ es probablemente el camino a seguir, pero si necesita algo un poco más complicado (y necesita reutilizarlo), el 'IEqualityComparer' podría ser mejor, especialmente si se implementa como un método de extensión. – bitxwise

1

Jon Skeet (como es habitual) tiene un método de extensión que le permite hacer esto: Can I specify my explicit type comparator inline?

+0

¡Eso es realmente genial! Probablemente no lo haga ahora, pero lo archivaré para más adelante. – wageoghe

+0

Hay muchas cargas de extensiones Linq. Hubiera usado ExceptBy, en este caso, pensar en ello: 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); 

Esto es trabajo para mí.

1

Otra solución utilizando su propio IEqualityComparer al igual que en la respuesta de @bitxwise y @DaveShaw, pero que no utilizan Except() que hace que sea un poco más simple:

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

Bueno, yo no sé si es una característica nueva en LINQ, pero eso es exactamente lo que hace .Union():

var d3 = d1.Union(d2); 

por supuesto, con Diccionarios tendrá que hacerle un comparador de igualdad a medida para que coincida con sólo las teclas:

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

y luego:

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

Con su ejemplo, la salida sería (probado en C# interactivo):

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

nota la diferencia:

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