Fusion de dictionnaires en C #

493

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.

orip
la source
175
Indépendant, mais pour ceux qui cherchent à fusionner seulement deux dictionnaires sans vérification des clés en double, cela fonctionne bien:dicA.Concat(dicB).ToDictionary(kvp => kvp.Key, kvp => kvp.Value)
Benjol
6
@Benjol vous auriez pu ajouter ceci dans la section des réponses
M.Kumaran
4
Fusion de Clojure en C #:dict1.Concat(dict2).GroupBy(p => p.Key).ToDictionary(g => g.Key, g => g.Last().Value)
Bruce
@Benjol: éliminez les doublons en préférant les entrées du premier dictionnaire avec dictA.Concat(dictB.Where(kvp => !dictA.ContainsKey(kvp.Key))).ToDictionary(kvp=> kvp.Key, kvp => kvp.Value).
Suncat2000

Réponses:

321

Cela dépend en partie de ce que vous voulez faire si vous rencontrez des doublons. Par exemple, vous pourriez faire:

var result = dictionaries.SelectMany(dict => dict)
                         .ToDictionary(pair => pair.Key, pair => pair.Value);

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:

var result = dictionaries.SelectMany(dict => dict)
                         .ToLookup(pair => pair.Key, pair => pair.Value)
                         .ToDictionary(group => group.Key, group => group.First());

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.

Jon Skeet
la source
3
Pour réellement fusionner les valeurs au lieu de simplement les extraire du premier dictionnaire, vous pouvez remplacer group => group.First () par group => group.SelectMany (value => value) dans la réponse modifiée de Jon Skeet.
andreialecu
3
Maintenant, je me demande que GroupBy serait mieux adapté que ToLookup? Je pense qu'ILookup ajoute juste un indexeur, une propriété de taille et contient une méthode en plus de IGrouping - donc ça doit être un tout petit peu plus rapide?
toong
2
@toong: Soit devrait être bien, pour être honnête. L'utilisation GroupBypourrait en effet être un peu plus efficace - mais je doute que ce soit significatif dans les deux cas.
Jon Skeet
1
@Joe: C'est assez simple en fournissant le comparateur de l' ToDictionaryappel 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.
Jon Skeet
1
@Serge: Je vous suggère de poser une nouvelle question avec des exigences précises.
Jon Skeet
265

Je le ferais comme ça:

dictionaryFrom.ToList().ForEach(x => dictionaryTo.Add(x.Key, x.Value));

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.

Jonas Stensved
la source
169
Si les doublons sont importants, utilisez dictionaryFrom.ToList().ForEach(x => dictionaryTo[x.Key] = x.Value). De cette façon, les valeurs de dictionaryFromremplacer la valeur d'une clé éventuellement existante.
okrumnow
7
Il est peu probable que cela soit plus rapide qu'une boucle - vous oubliez que ToList () finit par copier le dictionnaire. Plus rapide à coder cependant! ;-)
Søren Boisen
6
C'est plus rapide. Puisque ToList () ne finit pas par copier le dictionnaire, il copie juste les références des éléments à l'intérieur. Fondamentalement, l'objet de liste lui-même aura une nouvelle adresse en mémoire, les objets sont les mêmes !!
GuruC
4
Article de blog disparu mais yay Wayback machine: web.archive.org/web/20150311191313/http://diditwith.net/2006/10/…
CAD bloke
1
Hmm, mieux que la réponse de Jon Skeet, je présume.
Sнаđошƒаӽ
102

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

public static class DictionaryExtensions
{
    // Works in C#3/VS2008:
    // Returns a new dictionary of this ... others merged leftward.
    // Keeps the type of 'this', which must be default-instantiable.
    // Example: 
    //   result = map.MergeLeft(other1, other2, ...)
    public static T MergeLeft<T,K,V>(this T me, params IDictionary<K,V>[] others)
        where T : IDictionary<K,V>, new()
    {
        T newMap = new T();
        foreach (IDictionary<K,V> src in
            (new List<IDictionary<K,V>> { me }).Concat(others)) {
            // ^-- echk. Not quite there type-system.
            foreach (KeyValuePair<K,V> p in src) {
                newMap[p.Key] = p.Value;
            }
        }
        return newMap;
    }

}
Dave
la source
Bien joué. C'était exactement ce dont j'avais besoin. Belle utilisation des génériques. Je suis d'accord pour dire que la syntaxe est un peu maladroite, mais vous ne pouvez rien y faire.
Peter M
2
J'ai aimé cette solution, mais il y a une mise en garde: si le this T medictionnaire a été déclaré en utilisant new Dictionary<string, T>(StringComparer.InvariantCultureIgnoreCase)ou quelque chose de similaire, le dictionnaire résultant ne conservera pas le même comportement.
João Portela
3
Si vous faites meun Dictionary<K,V>, vous pouvez le faire var newMap = new Dictionary<TKey, TValue>(me, me.Comparer);, et vous évitez également le Tparamètre de type supplémentaire .
ANeves
2
Quelqu'un a-t-il un exemple de la façon d'utiliser cette bête?
Tim
2
@Tim: J'ai ajouté un exemple pour la bête ci-dessous, j'ai ajouté la solution par ANeves et le résultat est une bête plus domestiquée :)
keni
50

La solution triviale serait:

using System.Collections.Generic;
...
public static Dictionary<TKey, TValue>
    Merge<TKey,TValue>(IEnumerable<Dictionary<TKey, TValue>> dictionaries)
{
    var result = new Dictionary<TKey, TValue>();
    foreach (var dict in dictionaries)
        foreach (var x in dict)
            result[x.Key] = x.Value;
    return result;
}
orip
la source
22

Essayez ce qui suit

static Dictionary<TKey, TValue>
    Merge<TKey, TValue>(this IEnumerable<Dictionary<TKey, TValue>> enumerable)
{
    return enumerable.SelectMany(x => x).ToDictionary(x => x.Key, y => y.Value);
}
JaredPar
la source
19
Dictionary<String, String> allTables = new Dictionary<String, String>();
allTables = tables1.Union(tables2).ToDictionary(pair => pair.Key, pair => pair.Value);
ctrlalt313373
la source
1
Je me demandais simplement - Union ne fonctionnerait- elle pas simplement ? 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! :)
Mars Robertson
1
@Michal: Union renverra un 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.
Timothy
15

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

foreach (KeyValuePair<string,int> item in D2)
            {
                 D1[item.Key] = item.Value;
            }

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.

codingatty
la source
4
L'une des solutions les plus propres et les plus lisibles imo et évite également la ToList () que beaucoup d'autres utilisent.
404
15

Ce qui suit fonctionne pour moi. S'il y a des doublons, il utilisera la valeur de dictA.

public static IDictionary<TKey, TValue> Merge<TKey, TValue>(this IDictionary<TKey, TValue> dictA, IDictionary<TKey, TValue> dictB)
    where TValue : class
{
    return dictA.Keys.Union(dictB.Keys).ToDictionary(k => k, k => dictA.ContainsKey(k) ? dictA[k] : dictB[k]);
}
Ethan Reesor
la source
@EsbenSkovPedersen Je ne pense pas l'avoir rencontré quand j'ai répondu à cette question (nouveau dev C #)
Ethan Reesor
J'ai dû supprimer «where TValue: class» pour utiliser Guid comme valeur
om471987
@ om471987 Oui, Guid est une structure et non une classe, donc cela a du sens. Je pense qu'au départ, j'ai eu un problème lorsque je n'ai pas ajouté cet article. Je ne me souviens plus pourquoi.
Ethan Reesor
10

Voici une fonction d'aide que j'utilise:

using System.Collections.Generic;
namespace HelperMethods
{
    public static class MergeDictionaries
    {
        public static void Merge<TKey, TValue>(this IDictionary<TKey, TValue> first, IDictionary<TKey, TValue> second)
        {
            if (second == null || first == null) return;
            foreach (var item in second) 
                if (!first.ContainsKey(item.Key)) 
                    first.Add(item.Key, item.Value);
        }
    }
}
Andrew Harry
la source
Il y a beaucoup de bonnes solutions répertoriées ici, mais j'aime plutôt la simplicité de celle-ci. Juste ma préférence personnelle.
Jason
3
if(first == null)alors cette logique est inutile car firstn'est pas refet n'est pas retournée. Et cela ne peut pas être refparce 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).
Grault
@Jesdisciple - a supprimé le problème selon votre suggestion
Andrew Harry
8

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:

var result = dictionary1.Union(dictionary2).ToDictionary(k => k.Key, v => v.Value)

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.

var result = dictionary1.Union(dictionary2.Where(k => !dictionary1.ContainsKey(k.Key))).ToDictionary(k => k.Key, v => v.Value)

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:

var result = dictionaries.SelectMany(dict => dict)
                         .ToLookup(pair => pair.Key, pair => pair.Value)
                         .ToDictionary(group => group.Key, group => group.First());

la source
6

Que diriez-vous d'ajouter une paramssurcharge?

En outre, vous devez les saisir comme IDictionarypour une flexibilité maximale.

public static IDictionary<TKey, TValue> Merge<TKey, TValue>(IEnumerable<IDictionary<TKey, TValue>> dictionaries)
{
    // ...
}

public static IDictionary<TKey, TValue> Merge<TKey, TValue>(params IDictionary<TKey, TValue>[] dictionaries)
{
    return Merge((IEnumerable<TKey, TValue>) dictionaries);
}
Bryan Watts
la source
4
Il ajoute en quelque sorte aux autres réponses ici en notant que vous pouvez rendre la classe DictionaryExtensions encore plus agréable. Peut-être que cette question devrait devenir un wiki, ou une classe archivée pour git ....
Chris Moschini
5

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.

    public static void MergeOverwrite<T1, T2>(this IDictionary<T1, T2> dictionary, IDictionary<T1, T2> newElements)
    {
        if (newElements == null) return;

        foreach (var e in newElements)
        {
            dictionary.Remove(e.Key); //or if you don't want to overwrite do (if !.Contains()
            dictionary.Add(e);
        }
    }

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:

    public static void MergeOverwrite<T1, T2>(this ConcurrentDictionary<T1, T2> dictionary, IDictionary<T1, T2> newElements)
    {
        if (newElements == null || newElements.Count == 0) return;

        foreach (var ne in newElements)
        {
            dictionary.AddOrUpdate(ne.Key, ne.Value, (key, value) => value);
        }
    }

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 Countdictionnaire 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 ...

    public static IDictionary<T1, T2> MergeAllOverwrite<T1, T2>(IList<IDictionary<T1, T2>> allDictionaries)
    {
        var initSize = allDictionaries.Sum(d => d.Count);
        var resultDictionary = new Dictionary<T1, T2>(initSize);
        allDictionaries.ForEach(resultDictionary.MergeOverwrite);
        return resultDictionary;
    }

Notez que j'ai utilisé un IList<T>pour cette méthode ... principalement parce que si vous en utilisez un IEnumerable<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.

GoldPaintedLemons
la source
4

Basé sur les réponses ci-dessus, mais en ajoutant un paramètre Func pour permettre à l'appelant de gérer les doublons:

public static Dictionary<TKey, TValue> Merge<TKey, TValue>(this IEnumerable<Dictionary<TKey, TValue>> dicts, 
                                                           Func<IGrouping<TKey, TValue>, TValue> resolveDuplicates)
{
    if (resolveDuplicates == null)
        resolveDuplicates = new Func<IGrouping<TKey, TValue>, TValue>(group => group.First());

    return dicts.SelectMany<Dictionary<TKey, TValue>, KeyValuePair<TKey, TValue>>(dict => dict)
                .ToLookup(pair => pair.Key, pair => pair.Value)
                .ToDictionary(group => group.Key, group => resolveDuplicates(group));
}
toong
la source
4

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.

/// <summary>
/// Merges a dictionary against an array of other dictionaries.
/// </summary>
/// <typeparam name="TResult">The type of the resulting dictionary.</typeparam>
/// <typeparam name="TKey">The type of the key in the resulting dictionary.</typeparam>
/// <typeparam name="TValue">The type of the value in the resulting dictionary.</typeparam>
/// <param name="source">The source dictionary.</param>
/// <param name="mergeBehavior">A delegate returning the merged value. (Parameters in order: The current key, The current value, The previous value)</param>
/// <param name="mergers">Dictionaries to merge against.</param>
/// <returns>The merged dictionary.</returns>
public static TResult MergeLeft<TResult, TKey, TValue>(
    this TResult source,
    Func<TKey, TValue, TValue, TValue> mergeBehavior,
    params IDictionary<TKey, TValue>[] mergers)
    where TResult : IDictionary<TKey, TValue>, new()
{
    var result = new TResult();
    var sources = new List<IDictionary<TKey, TValue>> { source }
        .Concat(mergers);

    foreach (var kv in sources.SelectMany(src => src))
    {
        TValue previousValue;
        result.TryGetValue(kv.Key, out previousValue);
        result[kv.Key] = mergeBehavior(kv.Key, kv.Value, previousValue);
    }

    return result;
}
gxtaillon
la source
2

@Tim: devrait être un commentaire, mais les commentaires ne permettent pas de modifier le code.

Dictionary<string, string> t1 = new Dictionary<string, string>();
t1.Add("a", "aaa");
Dictionary<string, string> t2 = new Dictionary<string, string>();
t2.Add("b", "bee");
Dictionary<string, string> t3 = new Dictionary<string, string>();
t3.Add("c", "cee");
t3.Add("d", "dee");
t3.Add("b", "bee");
Dictionary<string, string> merged = t1.MergeLeft(t2, t2, t3);

Remarque: J'ai appliqué la modification de @ANeves à la solution de @Andrew Orsich, donc le MergeLeft ressemble à ceci maintenant:

public static Dictionary<K, V> MergeLeft<K, V>(this Dictionary<K, V> me, params IDictionary<K, V>[] others)
    {
        var newMap = new Dictionary<K, V>(me, me.Comparer);
        foreach (IDictionary<K, V> src in
            (new List<IDictionary<K, V>> { me }).Concat(others))
        {
            // ^-- echk. Not quite there type-system.
            foreach (KeyValuePair<K, V> p in src)
            {
                newMap[p.Key] = p.Value;
            }
        }
        return newMap;
    }
keni
la source
Proche de ce que j'ai écrit moi-même. Cela, bien sûr, ne fonctionnera que pour les Dictionarytypes. 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.
écraser le
Vous pouvez utiliser IDictionary au lieu de Dictionary
Mariusz Jamro
Vous obtiendrez toujours un Dictionaryobjet même si vous utilisez la méthode d'extension sur a ConcurrentDictionary. Cela pourrait entraîner des bogues difficiles à retracer.
Marcel
2

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

Dictionary<T1,T2> merged;
Dictionary<T1,T2> mergee;
mergee.ToList().ForEach(kvp => merged.Add(kvp.Key, kvp.Value));

ou

mergee.ToList().ForEach(kvp => merged.Append(kvp));
Cruces
la source
Désolé pour le downvote @Cruces, mais votre premier exemple de doublons répond à stackoverflow.com/a/6695211/704808 et votre deuxième exemple continue de rejeter le IEnumerable <KeyValuePair <string, string >> après avoir ajouté chaque élément à partir de mergee.
déversoir du
2

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

{
    // 2 dictionaries,  "b" key is common with different values

    var d1 = new Dictionary<string, int>() { { "a", 10 }, { "b", 21 } };
    var d2 = new Dictionary<string, int>() { { "c", 30 }, { "b", 22 } };

    var result1 = d1.Concat(d2).GroupBy(ele => ele.Key).ToDictionary(ele => ele.Key, ele => ele.First().Value);
    // result1 is  a=10, b=21, c=30    That is, took the "b" value of the first dictionary

    var result2 = d1.Concat(d2).GroupBy(ele => ele.Key).ToDictionary(ele => ele.Key, ele => ele.Last().Value);
    // result2 is  a=10, b=22, c=30    That is, took the "b" value of the last dictionary
}

Exemple 2

{
    // 3 dictionaries,  "b" key is common with different values

    var d1 = new Dictionary<string, int>() { { "a", 10 }, { "b", 21 } };
    var d2 = new Dictionary<string, int>() { { "c", 30 }, { "b", 22 } };
    var d3 = new Dictionary<string, int>() { { "d", 40 }, { "b", 23 } };

    var result1 = d1.Concat(d2).Concat(d3).GroupBy(ele => ele.Key).ToDictionary(ele => ele.Key, ele => ele.First().Value);
    // result1 is  a=10, b=21, c=30, d=40    That is, took the "b" value of the first dictionary

    var result2 = d1.Concat(d2).Concat(d3).GroupBy(ele => ele.Key).ToDictionary(ele => ele.Key, ele => ele.Last().Value);
    // result2 is  a=10, b=23, c=30, d=40    That is, took the "b" value of the last dictionary
}

Pour des scénarios plus complexes, voir d'autres réponses.
J'espère que cela a aidé.

Manohar Reddy Poreddy
la source
2
using System.Collections.Generic;
using System.Linq;

public static class DictionaryExtensions
{
    public enum MergeKind { SkipDuplicates, OverwriteDuplicates }
    public static void Merge<K, V>(this IDictionary<K, V> target, IDictionary<K, V> source, MergeKind kind = MergeKind.SkipDuplicates) =>
        source.ToList().ForEach(_ => { if (kind == MergeKind.OverwriteDuplicates || !target.ContainsKey(_.Key)) target[_.Key] = _.Value; });
}

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!

mattjs
la source
Réponse raffinée ci-dessous sans énumération binaire inutile quand un bool fera l'affaire ...
mattjs
2

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:

public static void Add<K, V>(this Dictionary<K, V> d, Dictionary<K, V> other) {
  foreach (var kvp in other)
  {
    if (!d.ContainsKey(kvp.Key))
    {
      d.Add(kvp.Key, kvp.Value);
    }
  }
}


var s0 = new Dictionary<string, string> {
  { "A", "X"}
};
var s1 = new Dictionary<string, string> {
  { "A", "X" },
  { "B", "Y" }
};
// Combine as many dictionaries and key pairs as needed
var a = new Dictionary<string, string> {
  s0, s1, s0, s1, s1, { "C", "Z" }
};
Andy
la source
1

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.

internal static class DictionaryExtensions
{
    public static Dictionary<T1, T2> Merge<T1, T2>(this Dictionary<T1, T2> first, Dictionary<T1, T2> second)
    {
        if (first == null) throw new ArgumentNullException("first");
        if (second == null) throw new ArgumentNullException("second");

        var merged = new Dictionary<T1, T2>();
        first.ToList().ForEach(kv => merged[kv.Key] = kv.Value);
        second.ToList().ForEach(kv => merged[kv.Key] = kv.Value);

        return merged;
    }
}

Usage:

Dictionary<string, string> merged = first.Merge(second);
Andrew Mikhailov
la source
1

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:

using System.Collections.Generic;
using System.Linq;

public static partial class Extensions
{
    public static void Merge<K, V>(this IDictionary<K, V> target, IDictionary<K, V> source, bool overwrite = false)
    {
        source.ToList().ForEach(_ => {
            if ((!target.ContainsKey(_.Key)) || overwrite)
                target[_.Key] = _.Value;
        });
    }
}
mattjs
la source
0

Fusion à l'aide d'un EqualityComparerqui mappe les éléments à comparer à une valeur / type différent. Ici, nous allons mapper de KeyValuePair(type d'élément lors de l'énumération d'un dictionnaire) vers Key.

public class MappedEqualityComparer<T,U> : EqualityComparer<T>
{
    Func<T,U> _map;

    public MappedEqualityComparer(Func<T,U> map)
    {
        _map = map;
    }

    public override bool Equals(T x, T y)
    {
        return EqualityComparer<U>.Default.Equals(_map(x), _map(y));
    }

    public override int GetHashCode(T obj)
    {
        return _map(obj).GetHashCode();
    }
}

Usage:

// if dictA and dictB are of type Dictionary<int,string>
var dict = dictA.Concat(dictB)
                .Distinct(new MappedEqualityComparer<KeyValuePair<int,string>,int>(item => item.Key))
                .ToDictionary(item => item.Key, item=> item.Value);
BSharp
la source
0

ou :

public static IDictionary<TKey, TValue> Merge<TKey, TValue>( IDictionary<TKey, TValue> x, IDictionary<TKey, TValue> y)
    {
        return x
            .Except(x.Join(y, z => z.Key, z => z.Key, (a, b) => a))
            .Concat(y)
            .ToDictionary(z => z.Key, z => z.Value);
    }

le résultat est une union où pour les entrées en double "y" gagne.

jtroconisa
la source
0
public static IDictionary<K, V> AddRange<K, V>(this IDictionary<K, V> one, IDictionary<K, V> two)
        {
            foreach (var kvp in two)
            {
                if (one.ContainsKey(kvp.Key))
                    one[kvp.Key] = two[kvp.Key];
                else
                    one.Add(kvp.Key, kvp.Value);
            }
            return one;
        }
mattylantz
la source
0

Une version de @ user166390 répond avec un IEqualityComparerparamètre ajouté pour permettre une comparaison de clé insensible à la casse.

    public static T MergeLeft<T, K, V>(this T me, params Dictionary<K, V>[] others)
        where T : Dictionary<K, V>, new()
    {
        return me.MergeLeft(me.Comparer, others);
    }

    public static T MergeLeft<T, K, V>(this T me, IEqualityComparer<K> comparer, params Dictionary<K, V>[] others)
        where T : Dictionary<K, V>, new()
    {
        T newMap = Activator.CreateInstance(typeof(T), new object[] { comparer }) as T;

        foreach (Dictionary<K, V> src in 
            (new List<Dictionary<K, V>> { me }).Concat(others))
        {
            // ^-- echk. Not quite there type-system.
            foreach (KeyValuePair<K, V> p in src)
            {
                newMap[p.Key] = p.Value;
            }
        }
        return newMap;
    }
gorillapower
la source
0
fromDic.ToList().ForEach(x =>
        {
            if (toDic.ContainsKey(x.Key))
                toDic.Remove(x.Key);
            toDic.Add(x);
        });
softwarevamp
la source