2011-01-25 9 views
7

Estoy probando el método UNION para fusionarme a diccionarios (de tipo Diccionario). Funciona bien con TValue type es string o int o incluso object. Pero si el tipo de TValue es una colección (probado con List y object []) se lanza una excepción: "ArgumentException: ya se ha agregado un elemento con la misma clave".Merge Dictionary <TKey, TValue> con el método Enumerable.Union

Aquí está mi código:

Dictionary<int,string> _dico1 = new Dictionary<int, string>() 
{ 
    {0, "zero"}, 
    {1, "one"} 
}; 

Dictionary<int,string> _dico2 = new Dictionary<int,string>() 
{ 
    {1 , "one"}, 
    {2 , "two"}, 
    {3 , "three"}, 
    {4 , "four"}, 
    {5 , "five"}, 
    {6 , "six"} 
}; 

Dictionary<int, List<string>> _dico3 = new Dictionary<int, List<string>>() 
{ 
    {0, new List<string>{"zero"}}, 
    {1, new List<string>{"one"}} 
}; 

Dictionary<int, List<string>> _dico4 = new Dictionary<int, List<string>>() 
{ 
    {1, new List<string>{"one"}}, 
    {2, new List<string>{"two"}}, 
    {3, new List<string>{"three"}}, 
    {4, new List<string>{"four"}}, 
    {5, new List<string>{"five"}}, 
    {6, new List<string>{"six"}}, 
}; 

    // works fine 
    var mergeDico = _dico1.Union(_dico2).ToDictionary(key => key.Key, value => value.Value); 

    // throw an ArgumentException : An item with the same key has already been added 
    var mergeDico2 = _dico3.Union(_dico4).ToDictionary(key => key.Key, value => value.Value); 

Por qué el comportamiento no es lo mismo? ¿Y cómo resolver este problema?

¡Gracias!

Respuesta

7

En el primer caso, la Unión está descartando las claves duplicadas porque los pares clave/valor son iguales. En el segundo caso, no lo son, porque List<String>{"one"} no es igual a otro List<string>{"one"}.

Sospecho que desea su llamada Union para usar un IEqualityComparer que solo tiene en cuenta las teclas dentro del diccionario.

+0

Así, en el primer caso, funciona porque el tipo TValue son los tipos de valor (int) o tipo inmutable (cadena) y, en el segundo caso, falla porque TValue es tipo de referencia? – Florian

+5

@Florian: No se debe directamente a la inmutabilidad, se debe a si los tipos implicados tienen semántica de igualdad de valores. Aún puede tener tipos inmutables que no hacen eso. –

2

Puede combinar segundo par de diccionarios con código como el siguiente:

 

var mergeDico2 = _dico3 
    .Concat(_dico4) 
    .GroupBy(_=> _.Key, _ => _.Value) 
    .ToDictionary(
     group => group.Key, 
     group => group.SelectMany(_ => _).ToList()); 
 

producirá un nuevo diccionario donde cada valor es el resultado de la concatenación de las listas de valores de ambos diccionarios. Si necesita sólo elementos distintos de las listas de llamada puede cambiar ToDictionary a éste:

 

var mergeDico2 = _dico3 
    .Concat(_dico4) 
    .GroupBy(_=> _.Key, _ => _.Value) 
    .ToDictionary(
     group => group.Key, 
     group => group.SelectMany(_ => _).Distinct().ToList()); 
 
1

Como ya se ha mencionado Jon que necesitamos para implementar IEqualityComparer a resolver el problema anterior. Aquí está el código de la forma en que se puede hacer:

IEqualityComparer:

public class MyEqualityComparer : IEqualityComparer<KeyValuePair<int,List<string>>> 
{ 
    public bool Equals(KeyValuePair<int, List<string>> x, KeyValuePair<int, List<string>> y) 
    { 
     //Let's say we are comparing the keys only. 
     return x.Key == y.Key; 
    } 

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

Uso:

Dictionary<int, List<string>> _dico3 = new Dictionary<int, List<string>>() 
    { 
     {0, new List<string> {"zero"}}, 
     {1, new List<string> {"one"}} 
    }; 

Dictionary<int, List<string>> _dico4 = new Dictionary<int, List<string>>() 
    { 
     {1, new List<string> {"one"}}, 
     {2, new List<string> {"two"}}, 
     {3, new List<string> {"three"}}, 
     {4, new List<string> {"four"}}, 
     {5, new List<string> {"five"}}, 
     {6, new List<string> {"six"}}, 
    }; 

Dictionary<int, List<string>> mergeDico2 = _dico3.Union(_dico4, new MyEqualityComparer()) 
    .ToDictionary(x => x.Key, x => x.Value); 
Cuestiones relacionadas