Pregunta Fusionando diccionarios en C #


¿Cuál es la mejor manera de combinar 2 o más diccionarios (Dictionary<T1,T2>) Cª#? (Las funciones 3.0 como LINQ están bien).

Estoy pensando en una firma de método en la línea de:

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

EDITAR: Tengo una solución genial de JaredPar y Jon Skeet, pero estaba pensando en algo que maneje llaves duplicadas. En caso de colisión, no importa qué valor se guarde en el dict, siempre que sea consistente.


373
2017-11-16 17:39


origen


Respuestas:


Esto en parte depende de lo que quiere que suceda si se encuentra con duplicados. Por ejemplo, podrías hacer:

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

Eso explotará si obtienes llaves duplicadas.

EDITAR: si usa ToLookup, obtendrá una búsqueda que puede tener múltiples valores por clave. Tú podría luego conviértalo a un diccionario:

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

Es un poco feo e ineficiente, pero es la forma más rápida de hacerlo en términos de código. (No lo he probado, lo admito).

Por supuesto, puede escribir su propio método de extensión ToDictionary2 (con un nombre mejor, pero no tengo tiempo para pensar en uno ahora); no es terriblemente difícil hacerlo, simplemente sobreescribiendo (o ignorando) las claves duplicadas. El bit importante (en mi opinión) es usar SelectMany, y darse cuenta de que un diccionario admite la iteración sobre sus pares clave / valor.


235
2017-11-16 17:46



Lo haría así:

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

Simple y fácil. De acuerdo a esta publicación en el blog es incluso más rápido que la mayoría de los bucles ya que su implementación subyacente accede a los elementos por índice en lugar de enumerador (mira esta respuesta).

Por supuesto, arrojará una excepción si hay duplicados, por lo que deberá verificar antes de fusionarse.


181
2017-07-14 14:51



Bueno, llegué tarde a la fiesta, pero esto es lo que uso. No explota si hay varias claves (las teclas "correctoras" reemplazan las teclas "lefter"), puede combinar varios diccionarios (si se desea) y conserva el tipo (con la restricción de que requiere un constructor público predeterminado 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;
    }

}

89
2018-04-21 02:05



La solución trivial sería:

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

41
2017-11-16 17:40



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

17
2018-04-06 15:28



Pruebe lo siguiente

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

17
2017-11-16 17:44



El siguiente funciona para mi. Si hay duplicados, usará el valor 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]);
}

13
2017-08-08 22:22



Aquí hay una función auxiliar que 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);
        }
    }
}

8
2017-08-06 04:26