Quelle est la meilleure façon de fusionner 2 dictionnaires (ou plus Dictionary<T1,T2>
) en C #? (3.0 fonctionnalités comme LINQ sont très bien).
Je pense à une signature de méthode dans le sens de:
public static Dictionary<TKey,TValue>
Merge<TKey,TValue>(Dictionary<TKey,TValue>[] dictionaries);
ou
public static Dictionary<TKey,TValue>
Merge<TKey,TValue>(IEnumerable<Dictionary<TKey,TValue>> dictionaries);
EDIT: J'ai obtenu une solution intéressante de JaredPar et Jon Skeet, mais je pensais à quelque chose qui gère les clés en double. En cas de collision, peu importe la valeur enregistrée dans le dict tant qu'elle est cohérente.
c#
dictionary
merge
orip
la source
la source
dicA.Concat(dicB).ToDictionary(kvp => kvp.Key, kvp => kvp.Value)
dict1.Concat(dict2).GroupBy(p => p.Key).ToDictionary(g => g.Key, g => g.Last().Value)
dictA.Concat(dictB.Where(kvp => !dictA.ContainsKey(kvp.Key))).ToDictionary(kvp=> kvp.Key, kvp => kvp.Value)
.Réponses:
Cela dépend en partie de ce que vous voulez faire si vous rencontrez des doublons. Par exemple, vous pourriez faire:
Cela lèvera une exception si vous obtenez des clés en double.
EDIT: Si vous utilisez ToLookup, vous obtiendrez une recherche qui peut avoir plusieurs valeurs par clé. Vous pouvez ensuite convertir cela en dictionnaire:
C'est un peu moche - et inefficace - mais c'est le moyen le plus rapide de le faire en termes de code. (Je ne l'ai pas testé, certes.)
Vous pouvez bien sûr écrire votre propre méthode d'extension ToDictionary2 (avec un meilleur nom, mais je n'ai pas le temps d'y penser maintenant) - ce n'est pas très difficile à faire, juste écraser (ou ignorer) les clés en double. Le plus important (à mon avis) est d'utiliser SelectMany et de se rendre compte qu'un dictionnaire prend en charge l'itération sur ses paires clé / valeur.
la source
GroupBy
pourrait en effet être un peu plus efficace - mais je doute que ce soit significatif dans les deux cas.ToDictionary
appel de méthode. Je préfère cependant garder la réponse plus simple pour le cas le plus courant, et j'espère que toute personne qui a besoin d'un comparateur personnalisé est capable de trouver la surcharge appropriée.Je le ferais comme ça:
Simple et facile. Selon ce billet de blog, il est encore plus rapide que la plupart des boucles car son implémentation sous-jacente accède aux éléments par index plutôt que par énumérateur (voir cette réponse) .
Bien sûr, il lèvera une exception s'il y a des doublons, vous devrez donc vérifier avant de fusionner.
la source
dictionaryFrom.ToList().ForEach(x => dictionaryTo[x.Key] = x.Value)
. De cette façon, les valeurs dedictionaryFrom
remplacer la valeur d'une clé éventuellement existante.Cela n'explose pas s'il y a plusieurs clés (les clés "plus droites" remplacent les clés "lefter"), peut fusionner un certain nombre de dictionnaires (si vous le souhaitez) et préserve le type (avec la restriction qu'il nécessite un constructeur public par défaut significatif):
la source
this T me
dictionnaire a été déclaré en utilisantnew Dictionary<string, T>(StringComparer.InvariantCultureIgnoreCase)
ou quelque chose de similaire, le dictionnaire résultant ne conservera pas le même comportement.me
unDictionary<K,V>
, vous pouvez le fairevar newMap = new Dictionary<TKey, TValue>(me, me.Comparer);
, et vous évitez également leT
paramètre de type supplémentaire .La solution triviale serait:
la source
Essayez ce qui suit
la source
la source
foreach(var kvp in Message.GetAttachments().Union(mMessage.GetImages()))
En l'utilisant dans le code de production, s'il y a des inconvénients, faites-le moi savoir! :)IEnumerable<KeyValuePair<TKey, TValue>>
où l'égalité est définie par l'égalité de la clé et de la valeur (il y a une surcharge pour permettre des alternatives). C'est très bien pour une boucle foreach, où c'est tout ce qui est nécessaire, mais il y a des cas où vous voulez réellement le dictionnaire.Je suis très en retard à la fête et je manque peut-être quelque chose, mais s'il n'y a pas de clés en double ou, comme le dit l'OP, "En cas de collision, peu importe la valeur qui est enregistrée dans le dict, tant qu'elle est cohérent, "quel est le problème avec celui-ci (fusion de D2 en D1)?
Cela semble assez simple, peut-être trop simple, je me demande si je manque quelque chose. C'est ce que j'utilise dans un code où je sais qu'il n'y a pas de clés en double. Je suis encore en test, donc j'aimerais savoir maintenant si j'oublie quelque chose, au lieu de le découvrir plus tard.
la source
Ce qui suit fonctionne pour moi. S'il y a des doublons, il utilisera la valeur de dictA.
la source
Voici une fonction d'aide que j'utilise:
la source
if(first == null)
alors cette logique est inutile carfirst
n'est pasref
et n'est pas retournée. Et cela ne peut pas êtreref
parce qu'il est déclaré comme une instance d'interface; vous devez soit supprimer la vérification des erreurs, soit la déclarer en tant qu'instance de classe, soit simplement renvoyer un nouveau dictionnaire (sans méthode d'extension).Option 1: cela dépend de ce que vous voulez faire si vous êtes sûr de ne pas avoir de clé en double dans les deux dictionnaires. que vous ne pourriez le faire:
Remarque: cela générera une erreur si vous obtenez des clés en double dans les dictionnaires.
Option 2: si vous pouvez avoir une clé en double, vous devrez gérer la clé en double avec la clause using of where.
Remarque: il n'obtiendra pas de clé en double. s'il y aura une clé en double, il obtiendra la clé de dictionary1.
Option 3: si vous souhaitez utiliser ToLookup. alors vous obtiendrez une recherche qui peut avoir plusieurs valeurs par clé. Vous pouvez convertir cette recherche en dictionnaire:
la source
Que diriez-vous d'ajouter une
params
surcharge?En outre, vous devez les saisir comme
IDictionary
pour une flexibilité maximale.la source
Compte tenu des performances des recherches et des suppressions de clés de dictionnaire car ce sont des opérations de hachage, et compte tenu du libellé de la question était la meilleure façon, je pense que ci-dessous est une approche parfaitement valide, et les autres sont un peu trop compliqués, à mon humble avis.
OU si vous travaillez dans une application multithread et que votre dictionnaire doit de toute façon être sûr pour les threads, vous devriez faire ceci:
Vous pouvez ensuite envelopper cela pour lui permettre de gérer une énumération de dictionnaires. Quoi qu'il en soit, vous regardez environ ~ O (3n) (toutes les conditions étant parfaites), car
.Add()
cela fera un autre, inutile mais pratiquement gratuit,Contains()
dans les coulisses. Je ne pense pas que ça va beaucoup mieux.Si vous souhaitez limiter les opérations supplémentaires sur les grandes collections, vous devez résumer le
Count
dictionnaire de chaque que vous êtes sur le point de fusionner et définir la capacité du dictionnaire cible à celle-ci, ce qui évite le coût ultérieur du redimensionnement. Donc, le produit final est quelque chose comme ça ...Notez que j'ai utilisé un
IList<T>
pour cette méthode ... principalement parce que si vous en utilisez unIEnumerable<T>
, vous vous êtes ouvert à plusieurs énumérations du même ensemble, ce qui peut être très coûteux si vous obtenez votre collection de dictionnaires d'un LINQ différé déclaration.la source
Basé sur les réponses ci-dessus, mais en ajoutant un paramètre Func pour permettre à l'appelant de gérer les doublons:
la source
La fête est presque morte maintenant, mais voici une version "améliorée" de user166390 qui a fait son chemin dans ma bibliothèque d'extensions. Outre quelques détails, j'ai ajouté un délégué pour calculer la valeur fusionnée.
la source
@Tim: devrait être un commentaire, mais les commentaires ne permettent pas de modifier le code.
Remarque: J'ai appliqué la modification de @ANeves à la solution de @Andrew Orsich, donc le MergeLeft ressemble à ceci maintenant:
la source
Dictionary
types. Surcharges pourraient être ajoutées pour d' autres types Dictionnaire (ConcurrentDictionary
,ReadOnlyDictionary
, etc.) Je ne pense pas que la création d'une nouvelle liste, et concat est personnellement nécessaire. Je viens d'itérer le tableau de paramètres en premier, puis d'itérer chaque KVP de chaque dictionnaire. Je ne vois aucun inconvénient.Dictionary
objet même si vous utilisez la méthode d'extension sur aConcurrentDictionary
. Cela pourrait entraîner des bogues difficiles à retracer.Je sais que c'est une vieille question, mais comme nous avons maintenant LINQ, vous pouvez le faire sur une seule ligne comme celle-ci
ou
la source
mergee
.J'ai eu peur de voir des réponses complexes, étant nouveau dans C #.
Voici quelques réponses simples.
Fusionner les dictionnaires d1, d2 et ainsi de suite .. et gérer les clés qui se chevauchent ("b" dans les exemples ci-dessous):
Exemple 1
Exemple 2
Pour des scénarios plus complexes, voir d'autres réponses.
J'espère que cela a aidé.
la source
Vous pouvez soit ignorer / ignorer (par défaut) ou écraser les doublons: et Bob est votre oncle à condition que vous ne soyez pas trop pointilleux sur les performances de Linq mais préférez plutôt un code maintenable concis comme je le fais: dans ce cas, vous pouvez supprimer le MergeKind.SkipDuplicates par défaut à appliquer un choix pour l'appelant et faire connaître au développeur les résultats!
la source
Notez que si vous utilisez une méthode d'extension appelée 'Ajouter', vous pouvez utiliser des initialiseurs de collection pour combiner autant de dictionnaires que nécessaire comme ceci:
la source
Fusion à l'aide d'une méthode d'extension. Il ne lève pas d'exception lorsqu'il y a des clés en double, mais remplace ces clés par des clés du deuxième dictionnaire.
Usage:
la source
Simplifié par rapport à ma réponse précédente avec une valeur bool par défaut de fusion non destructive si existante ou écrasée entièrement si vraie plutôt que d'utiliser une énumération Il convient toujours à mes propres besoins sans qu'aucun code plus sophistiqué ne soit jamais requis:
la source
Fusion à l'aide d'un
EqualityComparer
qui mappe les éléments à comparer à une valeur / type différent. Ici, nous allons mapper deKeyValuePair
(type d'élément lors de l'énumération d'un dictionnaire) versKey
.Usage:
la source
ou :
le résultat est une union où pour les entrées en double "y" gagne.
la source
la source
Une version de @ user166390 répond avec un
IEqualityComparer
paramètre ajouté pour permettre une comparaison de clé insensible à la casse.la source
la source