2011-01-25 1 views
7

Sto testando il metodo UNION per unire ai dizionari (di tipo Dizionario). Funziona perfettamente con il tipo TValue è stringa o int o anche oggetto. Ma se TValue type è una collezione (testata con List e object []) viene generata un'eccezione: "ArgumentException: un elemento con la stessa chiave è già stato aggiunto."Unisci dizionario <TKey, TValue> con Enumerable.Union metodo

Ecco il mio codice:

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

Perché il comportamento non è lo stesso? E come risolvere questo problema?

Grazie!

risposta

7

Nel primo caso, l'Unione sta scartando le chiavi duplicate perché le coppie chiave/valore sono uguali. Nel secondo caso non lo sono, perché uno List<String>{"one"} non è uguale a un altro List<string>{"one"}.

Ho il sospetto che vogliate chiamare la vostra chiamata Union per utilizzare uno IEqualityComparer che tiene conto solo delle chiavi all'interno del dizionario.

+0

Così, nel primo caso, funziona perché il tipo TValue sono tipi valore (INT) o tipo immutabile (stringa) e, nel secondo caso, fallisce perché TValue è un tipo di riferimento? – Florian

+5

@Florian: Non è direttamente dovuto all'immutabilità - è dovuto al fatto che i tipi coinvolti contengano la semantica dell'uguaglianza dei valori. Puoi ancora avere tipi immutabili che non lo fanno. –

2

È possibile unire seconda coppia di dizionari con codice come questo:

 

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

produrrà un nuovo dizionario in cui ogni valore è il risultato della concatenazione liste dai valori di entrambi i dizionari. Se avete bisogno solo elementi distinti di liste è possibile modificare chiamata ToDictionary a questo:

 

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

Come Jon detto già che abbiamo bisogno di implementare IEqualityComparer per risolvere il problema di cui sopra. Ecco il codice come si può fare:

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

Usage:

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