2010-10-25 6 views
17

Je développe une application en C# ciblant .NET 3.5. Dans celui-ci, j'ai 2 dictionnaires similaires qui contiennent des critères de validation pour un ensemble spécifique d'éléments dans mon application. Les deux dictionnaires ont des signatures identiques. Le premier dictionnaire a les paramètres par défaut et le deuxième dictionnaire contient certains paramètres définis par l'utilisateur. Je voudrais combiner les 2 dictionnaires en un qui contient les éléments des deux dictionnaires.C# Fusionner 2 dictionnaires

Le problème que je rencontre est qu'il est possible que les deux dictionnaires aient les mêmes valeurs de clé. La règle de base que je souhaite est d'avoir une combinaison des deux dictionnaire et s'il y a des clés dans les custom_settings qui existent déjà dans les paramètres default_settings, la valeur custom_settings écrase la valeur default_settings. La meilleure solution que j'ai est juste une boucle foreach, vérifiez si la clé existe dans l'autre dictionnaire, et sinon, ajoutez-la. J'ai fait quelques requêtes LINQ de base, mais je travaille toujours sur l'apprentissage des choses les plus avancées. J'ai vu quelques requêtes qui vont fusionner deux dictionnaires, mais la plupart impliquent de regrouper n'importe quel élément avec des clés dupliquées, ou seulement de retourner une collection avec seulement les clés dupliquées/Existe-t-il une requête ou une expression LINQ imitant le comportement de la boucle foreach J'utilise?

Répondre

38

Deux points:

  1. LINQ est pas idéal pour l'exécution d'effets secondaires. Dans ce cas, vous essayez de faire muter une collection existante plutôt que d'exécuter une requête, donc je ne voudrais pas utiliser une solution LINQ pure.
  2. Le setter on the generic dictionary's indexer a déjà pour effet d'ajouter la paire clé-valeur si la clé n'existe pas, ou d'écraser la valeur si c'est le cas.

Lorsque vous définissez la valeur de la propriété, si la clé est dans le dictionnaire, la valeur associée à cette clé est remplacée par la valeur assignée . Si la clé n'est pas dans le dictionnaire , les valeurs de clé et sont ajoutées au dictionnaire.

Ainsi, votre boucle foreach est essentiellement équivalente à:

foreach (var item in custom_settings) 
{ 
    default_settings[item.Key] = item.Value; 
} 

Maintenant que déjà est assez laconique, donc je ne pense pas que LINQ va vous aider à tout ce que beaucoup.

+3

+1 pour le point # 2 (http://msdn.microsoft.com/en-us/library/k7z0zy8k.aspx) – Brad

+1

+1 pour moi enseigner quelque chose que je ne savais pas. Je suis relativement autodidacte avec C# et je ne savais pas que vous pouviez le faire avec des dictionnaires. – psubsee2003

+0

@ psubsee2003: À votre santé. Je suis à peu près sûr d'avoir utilisé le même schéma que votre code avant de découvrir qu'il était redondant (par hasard, il se baladait avec un réflecteur). – Ani

2

Si vous allez faire beaucoup, alors je recommande à écrire un comparateur d'égalité pour les clés du dictionnaire:

private class KeyEqualityComparer<T, U> : IEqualityComparer<KeyValuePair<T, U>> 
{ 
    public bool Equals(KeyValuePair<T, U> x, KeyValuePair<T, U> y) 
    { 
     return x.Key.Equals(y.Key); 
    } 

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

Et puis à chaque fois que vous avez besoin de fusionner les dictionnaires, vous pouvez faire la

suivant
var comparer = new KeyEqualityComparer<string, MyElementSettings>(); 
dict1 = dict1.Union(dict2,comparer).ToDictionary(a => a.Key, b => b.Value); 
+0

celui-ci a fonctionné pour moi, même sans la classe/paramètre 'KeyEqualityComparer'. Merci – AceMark

1

Je pense que la réponse que j'ai choisi à l'origine est toujours la meilleure réponse pour ce cas particulier, je me suis retrouvé dans une autre situation similaire il y a un moment où j'avais 2 IEnumerable<> objets que je voulais convertir en un dictionnaire. une façon similaire, alors j'ai pensé que j'ajouterais cette solution ici pour aider quelqu'un dans le futur.Plutôt que de convertir à la fois un dictionnaire et d'utiliser la méthode dans la réponse sélectionnée, j'ai trouvé une nouvelle approche.

J'ai effectivement posté le initial solution sur SE-CodeReview et j'ai eu la suggestion de l'affiner davantage. Voici le code final je:

public Dictionary<String, Foo> Merge(XElement element1, XElement element2) 
{ 
    IEnumerable<Foo> firstFoos = GetXmlData(element1); // parse 1st set from XML 
    IEnumerable<Foo> secondFoos = GetXmlData(element2); // parse 2nd set from XML 

    var result = firstFoos.Union(secondFoos).ToDictionary(k=>k.Name, v=>v); 

    return result; 
} 

public class Foo 
{ 
    public String Name { get; } 

    // other Properties and Methods 
    // . 
    // . 
    // . 

    public override Boolean Equals(Object obj) 
    { 
     if (obj is Foo) 
     { 
      return this.Name == ((Foo)obj).Name;    
     } 

     return false; 
    } 
} 

La clé est Foo doit passer outre Equals() pour définir quels objets Foo peuvent être considérés comme en double, et les membres qui définissent quels objets sont en double devraient également être la clé Dictionary<> (en ce cas Name)

Si vous ne pouvez pas remplacer Equals() dans Foo, puis l'autre option consiste à utiliser Concat() et GroupBy() au lieu de Union()

public Dictionary<String, Foo> Merge(XElement element1, XElement element2) 
{ 
    IEnumerable<Foo> firstFoos = GetXmlData(element1); // parse 1st set from XML 
    IEnumerable<Foo> secondFoos = GetXmlData(element2); // parse 2nd set from XML 

    var result = firstFoos.Concat(secondFoos) 
          .GroupBy(foo => foo.Name) 
          .Select(grp => grp.First()) 
          .ToDictionary(k=>k.Name, v=>v); 

    return result; 
} 
8

Voici une belle méthode d'extension basée sur la réponse d'Ani.

public static class DictionaryExtensionMethods 
{ 
    public static void Merge<TKey, TValue>(this Dictionary<TKey, TValue> me, Dictionary<TKey, TValue> merge) 
    { 
     foreach (var item in merge) 
     { 
      me[item.Key] = item.Value; 
     } 
    } 
} 
+1

Je voudrais changer de dictionnaire sur IDictionary –

+0

S'il s'agit d'une collection de dictionnaire 'IEnumerable >' comment les faire correspondre? – barteloma

Questions connexes