Necesito fusionar varios diccionarios, esto es lo que tengo, por ejemplo:
dict1 = {1:{"a":{A}}, 2:{"b":{B}}}
dict2 = {2:{"c":{C}}, 3:{"d":{D}}
Con A
B
C
y D
siendo hojas del árbol, como{"info1":"value", "info2":"value2"}
Hay un nivel desconocido (profundidad) de diccionarios, podría ser {2:{"c":{"z":{"y":{C}}}}}
En mi caso, representa una estructura de directorio / archivos con nodos que son documentos y deja que sean archivos.
Quiero fusionarlos para obtener:
dict3 = {1:{"a":{A}}, 2:{"b":{B},"c":{C}}, 3:{"d":{D}}}
No estoy seguro de cómo podría hacer eso fácilmente con Python.
python
dictionary
merge
array-merge
fdhex
fuente
fuente
y
aplanar hasta elc
nivel o qué? Tu ejemplo está incompleto.Respuestas:
esto es realmente bastante complicado, especialmente si desea un mensaje de error útil cuando las cosas son inconsistentes, mientras acepta correctamente entradas duplicadas pero consistentes (algo que ninguna otra respuesta aquí ...)
suponiendo que no tenga un gran número de entradas, una función recursiva es más fácil:
tenga en cuenta que esto muta
a
: el contenido deb
se agrega aa
(que también se devuelve). si quieres quedartea
puedes llamarlo asímerge(dict(a), b)
.agf señaló (a continuación) que puede tener más de dos dictados, en cuyo caso puede usar:
donde todo se agregará a dict1.
[nota: edité mi respuesta inicial para mutar el primer argumento; eso hace que el "reducir" sea más fácil de explicar]
ps en python 3, también necesitarás
from functools import reduce
fuente
reduce
bucle o el equivalente para trabajar con un número arbitrario dedict
s en lugar de dos. Sin embargo, no estoy seguro de que esto hace lo que quiere, ya sea (que no estaba clara), Se termina con2: {'c': {'z': {'y': {'info1': 'value', 'info2': 'value2'}}}, 'b': {'info1': 'value', 'info2': 'value2'}}
su segundo ejemplo, no estoy seguro de si él quiere que elz
yy
aplanado o no?a[key] = a[key] + b[key]
. Gracias por la útil respuesta.copy.deepcopy
.Aquí hay una manera fácil de hacerlo usando generadores:
Esto imprime:
fuente
Un problema con esta pregunta es que los valores del dict pueden ser datos arbitrariamente complejos. En base a estas y otras respuestas, se me ocurrió este código:
Mi caso de uso es fusionar archivos YAML donde solo tengo que lidiar con un subconjunto de tipos de datos posibles. Por lo tanto, puedo ignorar las tuplas y otros objetos. Para mí, una lógica de fusión sensata significa
Todo lo demás y los imprevistos resultan en un error.
fuente
isinstance(a, (str, unicode, int, long, float))
isnt 'it?Como esta es la pregunta canónica (a pesar de ciertas no generalidades), estoy proporcionando el enfoque canónico Pythonic para resolver este problema.
Caso más simple: "las hojas son dictados anidados que terminan en dictos vacíos":
Este es el caso más simple para la recursividad, y recomendaría dos enfoques ingenuos:
Creo que preferiría el segundo al primero, pero tenga en cuenta que el estado original del primero tendría que ser reconstruido desde su origen. Aquí está el uso:
Caso complejo: "las hojas son de cualquier otro tipo:"
Entonces, si terminan en dictados, es un caso simple de fusionar los dictados vacíos finales. Si no, no es tan trivial. Si son cadenas, ¿cómo las fusionas? Los conjuntos se pueden actualizar de manera similar, por lo que podríamos dar ese tratamiento, pero perdemos el orden en que se fusionaron. Entonces, ¿importa el orden?
Entonces, en lugar de más información, el enfoque más simple será darles el tratamiento de actualización estándar si ambos valores no son dictados: es decir, el valor del segundo dict sobrescribirá al primero, incluso si el valor del segundo dict es None y el valor del primero es un dict con mucha información.
Y ahora
devoluciones
Aplicación a la pregunta original:
He tenido que eliminar las llaves alrededor de las letras y ponerlas entre comillas para que sea Python legítimo (de lo contrario, se establecerían literales en Python 2.7+), así como agregar una llave faltante:
y
rec_merge(dict1, dict2)
ahora vuelve:Que coincide con el resultado deseado de la pregunta original (después de cambiar, por ejemplo, el
{A}
para'A'
).fuente
Basado en @andrew cooke. Esta versión maneja listas anidadas de dictados y también permite la opción de actualizar los valores
fuente
Este simple procedimiento recursivo fusionará un diccionario en otro mientras anula las claves en conflicto:
Salida:
fuente
Basado en las respuestas de @andrew cooke. Se encarga de las listas anidadas de una mejor manera.
fuente
Si tiene un nivel desconocido de diccionarios, sugeriría una función recursiva:
fuente
Visión general
El siguiente enfoque subdivide el problema de una profunda fusión de dictos en:
Una función de fusión superficial parametrizada
merge(f)(a,b)
que utiliza una funciónf
para fusionar dos dictadosa
yb
Una función de fusión recursiva
f
para ser utilizada junto conmerge
Implementación
Una función para fusionar dos dictados (no anidados) se puede escribir de muchas maneras. Personalmente me gusta
Una buena manera de definir una función de fusión recursiva adecuada
f
es usar multipledispatch que permite definir funciones que evalúan a lo largo de diferentes rutas dependiendo del tipo de sus argumentos.Ejemplo
Para fusionar dos dictados anidados, simplemente use,
merge(f)
por ejemplo:Notas:
Las ventajas de este enfoque son:
La función se construye a partir de funciones más pequeñas que hacen una sola cosa, lo que hace que el código sea más simple para razonar y probar
El comportamiento no está codificado, pero puede cambiarse y ampliarse según sea necesario, lo que mejora la reutilización del código (consulte el ejemplo a continuación).
Personalización
Algunas respuestas también consideraron dictados que contienen listas, por ejemplo, de otros dictados (potencialmente anidados). En este caso, es posible que desee asignar un mapa sobre las listas y fusionarlas según la posición. Esto se puede hacer agregando otra definición a la función de fusión
f
:fuente
En caso de que alguien quiera otro enfoque para este problema, aquí está mi solución.
Virtudes : estilo corto, declarativo y funcional (recursivo, sin mutación).
Posible inconveniente : esta podría no ser la fusión que estás buscando. Consulte la cadena de documentación para semántica.
fuente
Podrías intentar fusionar en profundidad .
Instalación
Uso
fuente
Hay un pequeño problema con la respuesta de Andrew Cookes: en algunos casos modifica el segundo argumento
b
cuando modifica el dict devuelto. Específicamente es por esta línea:Si
b[key]
es adict
, simplemente se le asignaráa
, lo que significa que cualquier modificación posterior a esodict
afectará a ambosa
yb
.Para arreglar esto, la línea tendría que ser sustituida por esto:
Donde
clone_dict
es:Todavía. Obviamente, esto no tiene en cuenta
list
,set
y otras cosas, pero espero que ilustre las trampas al intentar fusionarsedicts
.Y para completar, aquí está mi versión, donde puedes pasarla múltiple
dicts
:fuente
deepcopy
lugar declone_dict
?Esta versión de la función tendrá en cuenta N número de diccionarios, y solo diccionarios: no se pueden pasar parámetros incorrectos, o generará un TypeError. La fusión en sí tiene en cuenta los conflictos clave y, en lugar de sobrescribir los datos de un diccionario más abajo en la cadena de fusión, crea un conjunto de valores y se agrega a eso; No se pierden datos.
Puede que no sea el más eficiente en la página, pero es el más completo y no perderá ninguna información cuando combine sus dictados de 2 a N.
salida: {1: [1, 2], 2: {1: 2, 3: 1}, 4: 4}
fuente
Dado que dictviews admite operaciones de conjunto, pude simplificar enormemente la respuesta de jterrace.
Cualquier intento de combinar un dict con un no dict (técnicamente, un objeto con un método de 'claves' y un objeto sin un método de 'claves') generará un AttributeError. Esto incluye tanto la llamada inicial a la función como las llamadas recursivas. Esto es exactamente lo que quería, así que lo dejé. Puede atrapar fácilmente un AttributeErrors arrojado por la llamada recursiva y luego obtener el valor que desee.
fuente
Corto y dulce:
Esto funciona como (y se basa en) el
dict.update
método de Python . RegresaNone
(siempre puede agregarreturn d
si lo prefiere) a medida que actualiza dictd
in situ. Keys inv
sobrescribirá cualquier clave existente end
(no intenta interpretar el contenido del dict).También funcionará para otras asignaciones ("dict-like").
fuente
El código dependerá de sus reglas para resolver conflictos de fusión, por supuesto. Aquí hay una versión que puede tomar un número arbitrario de argumentos y fusionarlos recursivamente a una profundidad arbitraria, sin usar ninguna mutación de objeto. Utiliza las siguientes reglas para resolver conflictos de fusión:
{"foo": {...}}
tiene prioridad sobre{"foo": "bar"}
){"a": 1}
,{"a", 2}
y{"a": 3}
el fin, el resultado será{"a": 3}
)fuente
Tenía dos diccionarios (
a
yb
) que podían contener cada uno cualquier número de diccionarios anidados. Quería fusionarlos recursivamente, conb
prioridad sobrea
.Considerando los diccionarios anidados como árboles, lo que quería era:
a
para que cada camino a cada hoja enb
esté representada ena
a
si se encuentra una hoja en la ruta correspondiente enb
b
los nodos de las hojas siguen siendo hojas.Las respuestas existentes fueron un poco complicadas para mi gusto y dejaron algunos detalles en el estante. Pirateé juntos lo siguiente, que pasa las pruebas unitarias para mi conjunto de datos.
Ejemplo (formateado para mayor claridad):
Los caminos
b
que necesitaban mantenerse eran:1 -> 'b' -> 'white'
2 -> 'd' -> 'black'
3 -> 'e'
.a
tenía las rutas únicas y no conflictivas de:1 -> 'a' -> 'red'
1 -> 'c' -> 'orange' -> 'dog'
así que todavía están representados en el mapa combinado.
fuente
Tengo una solución iterativa: funciona mucho mejor con grandes dictados y muchos de ellos (por ejemplo, jsons, etc.):
tenga en cuenta que esto usará el valor en d2 para anular d1, en caso de que no sean ambos dictados. (igual que el de pitón
dict.update()
)algunas pruebas:
He probado con alrededor de ~ 1200 dictos: este método tomó 0.4 segundos, mientras que la solución recursiva tomó ~ 2.5 segundos.
fuente
Esto debería ayudar a fusionar todos los elementos de
dict2
endict1
:Por favor, pruébelo y díganos si esto es lo que quería.
EDITAR:
La solución mencionada anteriormente combina solo un nivel, pero resuelve correctamente el ejemplo dado por OP. Para fusionar múltiples niveles, se debe utilizar la recursividad.
fuente
for k,v in dict2.iteritems(): dict1.setdefault(k,{}).update(v)
. Pero como señaló @agf, esto no combina los dictados anidados.{'a':'b'}
con{'a':{'c':'d'}
).He estado probando sus soluciones y decidí usar esta en mi proyecto:
Pasar funciones como parámetros es clave para extender la solución jterrace para que se comporte como todas las demás soluciones recursivas.
fuente
La forma más fácil que puedo pensar es:
Salida:
fuente
Tengo otra solución ligeramente diferente aquí:
De forma predeterminada, resuelve conflictos a favor de los valores del segundo dict, pero puede anular esto fácilmente, con algo de brujería puede incluso arrojar excepciones. :).
fuente
fuente
Hola, también tuve el mismo problema, pero pensé en una solución y lo publicaré aquí, en caso de que también sea útil para otros, básicamente fusionando diccionarios anidados y también agregando los valores, para mí necesitaba calcular algunas probabilidades para que esto uno funcionó muy bien:
Al utilizar el método anterior podemos fusionar:
target = {'6,6': {'6,63': 1}, '63, 4 ': {' 4,4 ': 1},' 4,4 ': {' 4,3 ': 1} , '6,63': {'63, 4 ': 1}}
src = {'5,4': {'4,4': 1}, '5,5': {'5,4': 1}, '4,4': {'4,3': 1} }
y esto se convertirá en: {'5,5': {'5,4': 1}, '5,4': {'4,4': 1}, '6,6': {'6,63' : 1}, '63, 4 ': {' 4,4 ': 1},' 4,4 ': {' 4,3 ': 2},' 6,63 ': {'63, 4': 1 }}
También observe los cambios aquí:
target = {'6,6': {'6,63': 1}, '6,63': {'63, 4 ': 1}, ' 4,4 ': {' 4,3 ': 1} , '63, 4 ': {' 4,4 ': 1}}
src = {'5,4': {'4,4': 1}, '4,3': {'3,4': 1}, '4,4': {'4,9': 1} , '3,4': {'4,4': 1}, '5,5': {'5,4': 1}}
fusionar = {'5,4': {'4,4': 1}, '4,3': {'3,4': 1}, '6,63': {'63, 4 ': 1} , '5,5': {'5,4': 1}, '6,6': {'6,63': 1}, '3,4': {'4,4': 1}, ' 63,4 ': {' 4,4 ': 1}, ' 4,4 ': {' 4,3 ': 1,' 4,9 ': 1} }
no olvide agregar también la importación para copiar:
fuente
Salida:
fuente
echa un vistazo al
toolz
paqueteda
fuente
La siguiente función combina b en a.
fuente
Y solo otra ligera variación:
Aquí hay una función de actualización profunda basada en un conjunto de python3 puro. Actualiza los diccionarios anidados recorriendo un nivel a la vez y se llama a sí mismo para actualizar cada siguiente nivel de valores del diccionario:
Un simple ejemplo:
fuente
¿Qué tal otra respuesta? Este también evita la mutación / efectos secundarios:
fuente