Unione dei dizionari in C #


493

Qual è il modo migliore per unire 2 o più dizionari (Dictionary<T1,T2> ) in C #? (3.0 funzioni come LINQ vanno bene).

Sto pensando a una firma del metodo sulla falsariga di:

public static Dictionary<TKey,TValue>
                 Merge<TKey,TValue>(Dictionary<TKey,TValue>[] dictionaries);

o

public static Dictionary<TKey,TValue>
                 Merge<TKey,TValue>(IEnumerable<Dictionary<TKey,TValue>> dictionaries);

EDIT: Ho ottenuto una soluzione interessante da JaredPar e Jon Skeet, ma stavo pensando a qualcosa che gestisce chiavi duplicate. In caso di collisione, non importa quale valore viene salvato nel dict purché sia ​​coerente.


175
Non correlato, ma per chiunque cerchi di unire solo due dizionari senza controlli duplicati delle chiavi, funziona bene:dicA.Concat(dicB).ToDictionary(kvp => kvp.Key, kvp => kvp.Value)
Benjol,

6
@Benjol avresti potuto aggiungere questo nella sezione di risposta
M.Kumaran,

4
L'unione di Clojure in C #:dict1.Concat(dict2).GroupBy(p => p.Key).ToDictionary(g => g.Key, g => g.Last().Value)
Bruce,

@Benjol: elimina i duplicati preferendo le voci del primo dizionario con dictA.Concat(dictB.Where(kvp => !dictA.ContainsKey(kvp.Key))).ToDictionary(kvp=> kvp.Key, kvp => kvp.Value).
Suncat2000

Risposte:


321

Questo dipende in parte da ciò che vuoi che accada se ti imbatti in duplicati. Ad esempio, potresti fare:

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

Ciò genererà un'eccezione se si ottengono chiavi duplicate.

EDIT: Se usi ToLookup, otterrai una ricerca che può avere più valori per chiave. Si potrebbe quindi convertire che a un dizionario:

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

È un po 'brutto - e inefficiente - ma è il modo più veloce per farlo in termini di codice. (Certo, non l'ho provato.)

Ovviamente potresti scrivere il tuo metodo di estensione ToDictionary2 (con un nome migliore, ma non ho tempo di pensarne uno ora) - non è terribilmente difficile da fare, basta sovrascrivere (o ignorare) le chiavi duplicate. Il bit importante (a mio avviso) sta usando SelectMany e realizzando che un dizionario supporta l'iterazione sulle sue coppie chiave / valore.


3
Per unire effettivamente i valori invece di prenderli dal primo dizionario è possibile sostituire group => group.First () con group => group.SelectMany (value => value) nella risposta modificata di Jon Skeet.
andreialecu,

3
Ora mi chiedo che GroupBy sarebbe più adatto di ToLookup? Penso che ILookup aggiunga solo un indicizzatore, una proprietà size e contenga un metodo oltre a IGrouping - quindi deve essere un po 'più veloce?
Toong

2
@toong: O dovrebbe andare bene, a dire il vero. L'uso GroupBypotrebbe effettivamente essere un po 'più efficiente, ma dubito che sarebbe significativo in entrambi i casi.
Jon Skeet,

1
@Joe: È abbastanza semplice fornendo il comparatore alla ToDictionarychiamata del metodo. Preferirei mantenere la risposta più semplice per il caso più comune e spero che chiunque abbia bisogno di un comparatore personalizzato sia in grado di trovare il sovraccarico rilevante.
Jon Skeet il

1
@Serge: ti suggerisco di porre una nuova domanda con requisiti precisi.
Jon Skeet,

265

Lo farei così:

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

Semplice e facile. Secondo questo post del blog è persino più veloce della maggior parte dei loop poiché l'implementazione sottostante accede agli elementi per indice anziché per enumeratore (vedi questa risposta) .

Ovviamente genererà un'eccezione se ci sono duplicati, quindi dovrai controllare prima di unire.


169
Se i duplicati contano, utilizzare dictionaryFrom.ToList().ForEach(x => dictionaryTo[x.Key] = x.Value). In questo modo i valori di dictionaryFromsovrascrivono il valore per una chiave eventualmente esistente.
Okrum, il

7
È improbabile che ciò sia più veloce di un ciclo: dimentichi che ToList () finisce per copiare il dizionario. Più veloce da programmare però! ;-)
Søren Boisen

6
Questo è più veloce .. Dato che ToList () in realtà non finisce per copiare il dizionario, copia semplicemente i riferimenti degli elementi al loro interno. Fondamentalmente l'oggetto elenco stesso avrà un nuovo indirizzo in memoria, gli oggetti sono uguali !!
GuruC,

4
Post del blog svanito ma yay Wayback machine: web.archive.org/web/20150311191313/http://diditwith.net/2006/10/…
CAD

1
Hmm, meglio che la risposta di Jon Skeet, presumo.
Sнаđошƒаӽ,

102

Questo non esplode se ci sono più chiavi (le chiavi "più a destra" sostituiscono le chiavi "lefter"), possono unire un numero di dizionari (se desiderato) e preservare il tipo (con la restrizione che richiede un costruttore pubblico predefinito significativo):

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

}

Molto bene. Questo era esattamente ciò di cui avevo bisogno. Bel uso dei generici. Concordato che la sintassi è un po 'imbarazzante, ma non puoi farci nulla.
Peter M,

2
Mi è piaciuta questa soluzione, ma c'è un avvertimento: se il this T medizionario è stato dichiarato usando new Dictionary<string, T>(StringComparer.InvariantCultureIgnoreCase)o qualcosa di simile, il dizionario risultante non manterrà lo stesso comportamento.
João Portela,

3
Se crei meun Dictionary<K,V>, puoi farlo var newMap = new Dictionary<TKey, TValue>(me, me.Comparer);, ed eviti anche il Tparametro extra type.
ANeves

2
Qualcuno ha un esempio di come usare questa bestia?
Tim

2
@Tim: ho aggiunto un esempio per la bestia di seguito, ho aggiunto la soluzione di ANeves e il risultato è una bestia più domestica :)
keni

50

La soluzione banale sarebbe:

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

22

Prova quanto segue

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

19
Dictionary<String, String> allTables = new Dictionary<String, String>();
allTables = tables1.Union(tables2).ToDictionary(pair => pair.Key, pair => pair.Value);

1
Mi stavo solo chiedendo: l' unione non funzionerebbe? foreach(var kvp in Message.GetAttachments().Union(mMessage.GetImages()))Usandolo nel codice di produzione, se ci sono degli svantaggi per favore fatemelo sapere! :)
Mars Robertson il

1
@Michal: Union restituirà un punto in IEnumerable<KeyValuePair<TKey, TValue>>cui l'uguaglianza è definita dall'uguaglianza di chiave e valore (c'è un sovraccarico per consentire alternative). Questo va bene per un ciclo foreach, dove questo è tutto ciò che è necessario, ma ci sono casi in cui si desidera effettivamente il dizionario.
Timoteo

15

Sono in ritardo alla festa e forse mi manca qualcosa, ma se o non ci sono chiavi duplicate o, come dice l'OP, "In caso di collisione, non importa quale valore viene salvato nel dict fintanto che è coerente, "cosa c'è di sbagliato in questo (fusione di D2 in D1)?

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

Sembra abbastanza semplice, forse troppo semplice, mi chiedo se mi manca qualcosa. Questo è quello che sto usando in alcuni codici dove so che non ci sono chiavi duplicate. Sono ancora in fase di test, quindi mi piacerebbe sapere ora se sto trascurando qualcosa, invece di scoprirlo in seguito.


4
Una delle soluzioni più pulite e leggibili imo ed evita anche la ToList () che molti altri usano.
404

15

Quanto segue funziona per me. Se ci sono duplicati, utilizzerà il valore di 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]);
}

@EsbenSkovPedersen Non credo di averlo incontrato quando ho risposto a questo (nuovo sviluppatore C #)
Ethan Reesor,

Ho dovuto rimuovere 'where TValue: class' per usare Guid come valore
om471987

@ om471987 Sì, Guid è una struttura non una classe, quindi ha senso. Penso che in origine avessi qualche problema quando non ho aggiunto quella clausola. Non ricordare più perché.
Ethan Reesor,

10

Ecco una funzione di supporto che uso:

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

Ci sono molte grandi soluzioni elencate qui, ma preferisco di più la semplicità di questa. Solo le mie preferenze personali.
Jason,

3
if(first == null)allora questa logica è inutile perché firstnon refè e non viene restituita. E non può essere refperché è dichiarato come istanza di interfaccia; è necessario rimuovere il controllo degli errori o dichiararlo come istanza di classe o semplicemente restituire un nuovo dizionario (senza metodo di estensione).
Grault,

@Jesdisciple - rimosso il problema secondo il tuo suggerimento
Andrew Harry,

8

Opzione 1: dipende da cosa vuoi che accada se sei sicuro di non avere una chiave duplicata in entrambi i dizionari. di quanto potresti fare:

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

Nota: questo genererà un errore se si ottengono chiavi duplicate nei dizionari.

Opzione 2: se puoi avere una chiave duplicata, dovrai gestire una chiave duplicata usando la clausola where.

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

Nota: non otterrà la chiave duplicata. se ci sarà una chiave duplicata rispetto a quella otterrà la chiave del dizionario1.

Opzione 3: se si desidera utilizzare ToLookup. quindi otterrai una ricerca che può avere più valori per chiave. È possibile convertire quella ricerca in un dizionario:

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

6

Che ne dici di aggiungere un paramssovraccarico?

Inoltre, dovresti digitarli come IDictionaryper la massima flessibilità.

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

4
Sta aggiungendo altre risposte qui notando che puoi rendere la classe DictionaryExtensions ancora più bella. Forse questa domanda dovrebbe diventare un wiki, o una classe registrata per git ....
Chris Moschini,

5

Considerando le prestazioni delle ricerche e delle cancellazioni dei tasti del dizionario poiché sono operazioni di hash e considerando che la formulazione della domanda è stata il modo migliore , penso che di seguito sia un approccio perfettamente valido e gli altri sono un po 'troppo complicati, IMHO.

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

OPPURE se stai lavorando in un'applicazione multithread e il tuo dizionario deve comunque essere thread-safe, dovresti farlo:

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

È quindi possibile racchiuderlo per farlo gestire un elenco di dizionari. Indipendentemente da ciò, stai guardando a ~ O (3n) (tutte le condizioni sono perfette), dal momento .Add()che farà un ulteriore, inutile ma praticamente gratuito,Contains() dietro le quinte. Non penso che sia molto meglio.

Se si desidera limitare le operazioni extra su raccolte di grandi dimensioni, è necessario riassumere il Countdizionario di ciascun dizionario che si sta per unire e impostare la capacità del dizionario di destinazione su quella, evitando così i successivi costi di ridimensionamento. Quindi, il prodotto finale è qualcosa di simile ...

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

Nota che ho seguito IList<T>questo metodo ... soprattutto perché se ne prendi uno IEnumerable<T>, ti sei aperto a più enumerazioni dello stesso set, il che può essere molto costoso se ottieni la tua raccolta di dizionari da un LINQ differito dichiarazione.


4

Sulla base delle risposte precedenti, ma aggiungendo un parametro Func per consentire al chiamante di gestire i duplicati:

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

4

La festa è praticamente morta ora, ma ecco una versione "migliorata" di user166390 che si è fatta strada nella mia libreria di estensioni. A parte alcuni dettagli, ho aggiunto un delegato per calcolare il valore unito.

/// <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;
}

2

@Tim: dovrebbe essere un commento, ma i commenti non consentono la modifica del codice.

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

Nota: ho applicato la modifica di @ANeves alla soluzione di @Andrew Orsich, quindi MergeLeft ora appare così:

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

Vicino a quello che ho scritto da solo. Questo, spesso, funzionerà solo per i Dictionarytipi. Sovraccarichi potrebbero essere aggiunte per altri tipi Dictionary ( ConcurrentDictionary, ReadOnlyDictionary, ecc) non credo che la creazione di un nuovo elenco, e concat è necessaria personalmente. Ho appena ripetuto per primo l'array params, quindi ho ripetuto ogni KVP di ciascun dizionario. Non vedo un inconveniente.
schiaccia il

Puoi usare IDictionary invece di Dizionario
Mariusz Jamro,

Otterresti sempre un Dictionaryoggetto anche se hai utilizzato il metodo di estensione su a ConcurrentDictionary. Ciò potrebbe portare a bug difficili da rintracciare.
Marcel,

2

So che questa è una vecchia domanda, ma dal momento che ora abbiamo LINQ puoi farlo in una sola riga come questa

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

o

mergee.ToList().ForEach(kvp => merged.Append(kvp));

Ci scusiamo per il downvote @Cruces, ma i tuoi duplicati del primo esempio rispondono a stackoverflow.com/a/6695211/704808 e il tuo secondo esempio continua a scartare IEnumerable <KeyValuePair <string, string >> espanso dopo aver aggiunto ogni elemento da mergee.
diga

2

Ho avuto paura di vedere risposte complesse, essendo nuovo a C #.

Ecco alcune semplici risposte.
Unione di dizionari d1, d2 e così via .. e gestione di eventuali tasti sovrapposti ("b" negli esempi seguenti):

Esempio 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
}

Esempio 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
}

Per scenari più complessi, vedi altre risposte.
Spero che abbia aiutato.


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

Puoi saltare / ignorare (impostazione predefinita) o sovrascrivere i duplicati: E Bob è tuo zio a condizione che tu non sia eccessivamente esigente riguardo alle prestazioni di Linq ma preferisca invece un codice gestibile conciso come faccio io: in tal caso puoi rimuovere il valore predefinito MergeKind.SkipDuplicates per applicare una scelta per il chiamante e rendere lo sviluppatore consapevole di quali saranno i risultati!


Raffinata risposta di seguito senza
enumerazione

2

Nota che se usi un metodo di estensione chiamato 'Aggiungi', puoi usare gli inizializzatori di raccolta per combinare tutti i dizionari necessari in questo modo:

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" }
};

1

Unione mediante un metodo di estensione. Non genera eccezioni quando ci sono chiavi duplicate, ma sostituisce quelle chiavi con le chiavi del secondo dizionario.

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

Uso:

Dictionary<string, string> merged = first.Merge(second);

1

Semplificato dall'uso rispetto alla mia precedente risposta con un valore predefinito di unione non distruttiva se esistente o sovrascritto interamente se vero anziché utilizzare un enum. Si adatta ancora alle mie esigenze senza che sia mai stato richiesto alcun codice di fantasia:

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

0

Fusione utilizzando un EqualityComparerelemento che mappa gli elementi per il confronto con un valore / tipo diverso. Qui KeyValuePaireseguiremo la mappatura da (tipo di elemento durante l'enumerazione di un dizionario) a 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();
    }
}

Uso:

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

0

o :

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

il risultato è un'unione dove vince per le voci duplicate "y".


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

0

Una versione di @ user166390 risponde con un IEqualityComparerparametro aggiunto per consentire il confronto delle chiavi senza distinzione tra maiuscole e minuscole.

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

0
fromDic.ToList().ForEach(x =>
        {
            if (toDic.ContainsKey(x.Key))
                toDic.Remove(x.Key);
            toDic.Add(x);
        });
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.