¿Cuál es la mejor manera de combinar 2 o más diccionarios ( Dictionary<T1,T2>
) en C #? (3.0 características 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: obtuve una solución genial de JaredPar y Jon Skeet, pero estaba pensando en algo que maneje claves duplicadas. En caso de colisión, no importa qué valor se guarde en el dict mientras sea coherente.
c#
dictionary
merge
orip
fuente
fuente
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)
.Respuestas:
Esto depende en parte de lo que quieras que suceda si te encuentras con duplicados. Por ejemplo, podrías hacer:
Eso arrojará una excepción si obtiene claves duplicadas.
EDITAR: si usa ToLookup, obtendrá una búsqueda que puede tener múltiples valores por clave. Usted podría luego convertir eso a un diccionario:
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, podría 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 de hacer, simplemente sobrescribiendo (o ignorando) las claves duplicadas. Lo importante (en mi opinión) es usar SelectMany y darse cuenta de que un diccionario admite la iteración sobre sus pares clave / valor.
fuente
GroupBy
podría ser un poco más eficiente, pero dudo que sea significativo de cualquier manera.ToDictionary
llamada al método. Sin embargo, preferiría mantener la respuesta más simple para el caso más común, y espero que cualquiera que necesite un comparador personalizado sea capaz de encontrar la sobrecarga relevante.Lo haría así:
Simple y fácil. De acuerdo con esta publicación de blog , es aún 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 (vea esta respuesta) .
Por supuesto, arrojará una excepción si hay duplicados, por lo que deberá verificar antes de fusionar.
fuente
dictionaryFrom.ToList().ForEach(x => dictionaryTo[x.Key] = x.Value)
. De esta manera, los valores dedictionaryFrom
anulan el valor de una clave posiblemente existente.Esto no explota si hay varias claves (las teclas "más correctas" reemplazan a las claves "posteriores"), puede combinar varios diccionarios (si lo desea) y conserva el tipo (con la restricción de que requiere un constructor público predeterminado significativo):
fuente
this T me
diccionario se declaró usandonew Dictionary<string, T>(StringComparer.InvariantCultureIgnoreCase)
o algo similar, el diccionario resultante no conservará el mismo comportamiento.me
unDictionary<K,V>
, puede hacerlovar newMap = new Dictionary<TKey, TValue>(me, me.Comparer);
, y también evita elT
parámetro de tipo adicional .La solución trivial sería:
fuente
Intenta lo siguiente
fuente
fuente
foreach(var kvp in Message.GetAttachments().Union(mMessage.GetImages()))
Utilizándolo en el código de producción, si hay inconvenientes, ¡hágamelo saber! :)IEnumerable<KeyValuePair<TKey, TValue>>
donde la igualdad se define por la igualdad de clave y valor (hay una sobrecarga para permitir alternativas). Esto está bien para un bucle foreach, donde esto es todo lo que es necesario, pero hay casos en los que realmente desea el diccionario.Llego muy tarde a la fiesta y tal vez me falte algo, pero si no hay claves duplicadas o, como dice el OP, "En caso de colisión, no importa qué valor se guarde en el dict mientras sea coherente, "¿qué tiene de malo este (fusionar D2 en D1)?
Parece bastante simple, tal vez demasiado simple, me pregunto si me estoy perdiendo algo. Esto es lo que estoy usando en algún código donde sé que no hay claves duplicadas. Sin embargo, todavía estoy en pruebas, así que me encantaría saber ahora si estoy pasando por alto algo, en lugar de averiguarlo más tarde.
fuente
Lo siguiente funciona para mí. Si hay duplicados, usará el valor de dictA.
fuente
Aquí hay una función auxiliar que uso:
fuente
if(first == null)
entonces esta lógica es inútil porquefirst
noref
es y no se devuelve. Y no puede serref
porque se declara como una instancia de interfaz; debe eliminar la comprobación de errores o declararla como una instancia de clase o simplemente devolver un nuevo diccionario (sin método de extensión).Opción 1: Esto depende de lo que quiera que suceda si está seguro de que no tiene una clave duplicada en ambos diccionarios. de lo que podrías hacer:
Nota: Esto arrojará un error si obtiene claves duplicadas en los diccionarios.
Opción 2: si puede tener una clave duplicada, deberá manejar la clave duplicada con el uso de la cláusula where.
Nota: No obtendrá una clave duplicada. si habrá alguna clave duplicada, entonces obtendrá la clave del diccionario1.
Opción 3: si desea utilizar ToLookup. entonces obtendrá una búsqueda que puede tener múltiples valores por clave. Podrías convertir esa búsqueda en un diccionario:
fuente
¿Qué tal agregar una
params
sobrecarga?Además, debe escribirlos
IDictionary
para obtener la máxima flexibilidad.fuente
Teniendo en cuenta el rendimiento de las búsquedas y eliminaciones de claves del diccionario, ya que son operaciones hash, y teniendo en cuenta la redacción de la pregunta fue la mejor manera, creo que a continuación es un enfoque perfectamente válido, y los otros son un poco demasiado complicados, en mi humilde opinión.
O bien, si está trabajando en una aplicación multiproceso y su diccionario debe ser seguro para subprocesos de todos modos, debería hacer esto:
Luego, podría ajustar esto para que maneje una enumeración de diccionarios. De todos modos, estás viendo aproximadamente ~ O (3n) (todas las condiciones son perfectas), ya
.Add()
que harán unContains()
detrás de escena adicional, innecesario pero prácticamente gratis . No creo que mejore mucho.Si desea limitar las operaciones adicionales en colecciones grandes, debe resumir el
Count
de cada diccionario que está a punto de fusionar y establecer la capacidad del diccionario de destino, lo que evita el costo posterior de cambiar el tamaño. Entonces, el producto final es algo como esto ...Tenga en cuenta que acepté
IList<T>
este método ... sobre todo porque si acepta unIEnumerable<T>
, se ha abierto a múltiples enumeraciones del mismo conjunto, lo que puede ser muy costoso si obtiene su colección de diccionarios de un LINQ diferido declaración.fuente
Basado en las respuestas anteriores, pero agregando un parámetro Func para permitir que la persona que llama maneje los duplicados:
fuente
La fiesta está bastante muerta ahora, pero aquí hay una versión "mejorada" de user166390 que llegó a mi biblioteca de extensiones. Además de algunos detalles, agregué un delegado para calcular el valor combinado.
fuente
@Tim: debería ser un comentario, pero los comentarios no permiten la edición de código.
Nota: Apliqué la modificación de @ANeves a la solución de @Andrew Orsich, por lo que MergeLeft se ve así ahora:
fuente
Dictionary
tipos. Sobrecargas podrían añadirse otros tipos de diccionario (ConcurrentDictionary
,ReadOnlyDictionary
, etc) No creo que la creación de una nueva lista, y concat es necesario personalmente. Primero itere la matriz de params, luego itere cada KVP de cada diccionario. No veo un retroceso.Dictionary
objeto incluso si utilizara el método de extensión en aConcurrentDictionary
. Eso podría conducir a errores difíciles de rastrear.Sé que esta es una pregunta antigua, pero como ahora tenemos LINQ, puede hacerlo en una sola línea como esta.
o
fuente
mergee
.Me asustó ver respuestas complejas, siendo nuevo en C #.
Aquí hay algunas respuestas simples.
Fusionando d1, d2, etc., diccionarios y maneje las teclas superpuestas ("b" en los ejemplos a continuación):
Ejemplo 1
Ejemplo 2
Para escenarios más complejos, vea otras respuestas.
Espero que haya ayudado.
fuente
Puede omitir / ignorar (predeterminado) o sobrescribir los duplicados: y Bob es su tío, siempre que no sea demasiado quisquilloso con el rendimiento de Linq, sino que prefiera un código de mantenimiento conciso como lo hago yo: en cuyo caso puede eliminar el MergeKind.SkipDuplicates predeterminado para imponer ¡una opción para la persona que llama y hacer que el desarrollador conozca cuáles serán los resultados!
fuente
Tenga en cuenta que si usa un método de extensión llamado 'Agregar', puede usar inicializadores de colección para combinar tantos diccionarios como sea necesario de esta manera:
fuente
Fusionar usando un método de extensión. No arroja excepciones cuando hay claves duplicadas, sino que las reemplaza con claves del segundo diccionario.
Uso:
fuente
Simplificado del uso en comparación con mi respuesta anterior con un valor predeterminado de fusión no destructiva si existe o sobrescribir completamente si es verdadero en lugar de usar una enumeración. Todavía se adapta a mis propias necesidades sin que se requiera ningún código más elegante:
fuente
Fusionar usando un elemento
EqualityComparer
que asigna elementos para compararlos con un valor / tipo diferente. Aquí asignaremos desdeKeyValuePair
(tipo de elemento al enumerar un diccionario) aKey
.Uso:
fuente
o:
el resultado es una unión donde para entradas duplicadas "y" gana.
fuente
fuente
Una versión de @ user166390 responde con un
IEqualityComparer
parámetro agregado para permitir la comparación de teclas sin distinción entre mayúsculas y minúsculas.fuente
fuente