[C#] Unione di dizionari in C #


Answers

Lo farei così:

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

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

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

Question

Qual è il modo migliore per unire 2 o più dizionari ( Dictionary<T1,T2> ) in C #? (Funzionalità 3.0 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 trovato una soluzione interessante da JaredPar e Jon Skeet, ma stavo pensando a qualcosa che gestisse chiavi duplicate. In caso di collisione, non importa quale valore viene salvato per la dict finché è coerente.




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



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 in cui per le voci duplicate "y" vince.




Ecco una funzione di aiuto che utilizzo:

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



In base alle risposte sopra, 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));
}



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



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



Sono in ritardo per la festa e forse mi manca qualcosa, ma se non ci sono chiavi duplicate o, come dice l'OP, "In caso di collisione, non importa quale valore viene salvato per la dict finché è 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 manchi qualcosa. Questo è quello che sto usando in qualche codice dove so che non ci sono chiavi duplicate. Sono ancora in fase di test, però, quindi mi piacerebbe sapere ora se sto trascurando qualcosa, invece di scoprirlo più tardi.




So che questa è una vecchia domanda, ma dal momento che ora abbiamo LINQ puoi farlo in una singola 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));