¿Cómo fusionar varios dictados con la misma clave?


83

Tengo varios dicts / pares clave-valor como este:

d1 = {key1: x1, key2: y1}  
d2 = {key1: x2, key2: y2}  

Quiero que el resultado sea un nuevo dictado (de la manera más eficiente, si es posible):

d = {key1: (x1, x2), key2: (y1, y2)}  

En realidad, quiero que el resultado d sea:

d = {key1: (x1.x1attrib, x2.x2attrib), key2: (y1.y1attrib, y2.y2attrib)}  

Si alguien me muestra cómo obtener el primer resultado, puedo averiguar el resto.


3
@Salil: ¿Podemos asumir que cada clave está presente en todos los diccionarios?
Björn Pollex


Hola Space_C0wb0y, sí, las claves están presentes en todos los diccionarios.
Salil

Es absolutamente crucial especificar si todos los dictados tienen las mismas teclas.
yugr

Respuestas:


44

asumiendo que todas las claves están siempre presentes en todos los dictados:

ds = [d1, d2]
d = {}
for k in d1.iterkeys():
    d[k] = tuple(d[k] for d in ds)

Nota: En Python 3.x use el siguiente código:

ds = [d1, d2]
d = {}
for k in d1.keys():
  d[k] = tuple(d[k] for d in ds)

y si el dic contiene matrices numpy:

ds = [d1, d2]
d = {}
for k in d1.keys():
  d[k] = np.concatenate(list(d[k] for d in ds))

3
Creo que sólo "para k en d1".
Salil

y d.get (k, None) en lugar de d [k]
tahir

1
@tahir Esto significaría que los dictados tienen claves que no coinciden, por lo que la iteración d1no es correcta (puede perder claves en otros dictados).
yugr

1
Para usuarios de Python 3: d1.iterkeys () = d1.items ()
Riley

Todavía no me funciona en Python3.x. Intenté esto incluso si mis valores no son matrices, y funciona. Sin embargo, los valores de salida serán matrices. stackoverflow.com/questions/54040858/…
Ric S

73

Aquí hay una solución general que manejará una cantidad arbitraria de diccionarios, con casos en los que las claves están solo en algunos de los diccionarios:

from collections import defaultdict

d1 = {1: 2, 3: 4}
d2 = {1: 6, 3: 7}

dd = defaultdict(list)

for d in (d1, d2): # you can list as many input dicts as you want here
    for key, value in d.items():
        dd[key].append(value)

print(dd)

Muestra:

defaultdict(<type 'list'>, {1: [2, 6], 3: [4, 7]})

Además, para obtener su .attrib, simplemente cambie append(value)aappend(value.attrib)


Creo que el OP quiere los valores como tupleno list.
user225312

1
@AA: ¿realmente importa? las tuplas serán más difíciles de construir en el caso más general de dictados de entrada múltiple donde algunas teclas no están presentes en todas partes, en mi humilde opinión
Eli Bendersky

1
A continuación, puede que desee hacer una normal, dictfuera de la defaultdictpor lo que tiene normal de dictcomportamiento para las llaves inexistentes, etc: dd = dict(dd)
Ned Deily

@Ned: buen punto, pero depende del uso eventual de los datos
Eli Bendersky

@Eli: No, no importa, pero solo estaba tratando de basarlo en lo que quería el OP y esperaba que hubiera una solución para las tuplas de usted :-)
user225312

4

Si solo tiene d1 y d2,

from collections import defaultdict

d = defaultdict(list)
for a, b in d1.items() + d2.items():
    d[a].append(b)

4
dict1 = {'m': 2, 'n': 4}
dict2 = {'n': 3, 'm': 1}

Asegurándose de que las claves estén en el mismo orden:

dict2_sorted = {i:dict2[i] for i in dict1.keys()}

keys = dict1.keys()
values = zip(dict1.values(), dict2_sorted.values())
dictionary = dict(zip(keys, values))

da:

{'m': (2, 1), 'n': (4, 3)}

1
El orden de los elementos en values()no está definido, por lo que es posible que esté fusionando valores de claves no relacionadas.
yugr

Acabo de aplicar los cambios para que ahora pueda capturar sus comentarios
Mahdi Ghelichi

No creo que el cambio solucione el problema. Necesita usar sorted(d.items())o sorted(d.keys())lograr resultados predecibles.
yugr

¿Puede dar un ejemplo que demuestre lo contrario? dict2_sorted es un diccionario ordenado en Python.
Mahdi Ghelichi

El hecho de que funcione para diccionarios pequeños en su máquina no es una prueba. Mira la reproducción aquí .
yugr

3

Aquí hay un enfoque que puede usar y que funcionaría incluso si ambos dictonarios no tienen las mismas claves:

d1 = {'a':'test','b':'btest','d':'dreg'}
d2 = {'a':'cool','b':'main','c':'clear'}

d = {}

for key in set(d1.keys() + d2.keys()):
    try:
        d.setdefault(key,[]).append(d1[key])        
    except KeyError:
        pass

    try:
        d.setdefault(key,[]).append(d2[key])          
    except KeyError:
        pass

print d

Esto generaría la siguiente entrada:

{'a': ['test', 'cool'], 'c': ['clear'], 'b': ['btest', 'main'], 'd': ['dreg']}

¿Se set(d1.keys() + d2.keys()) puede cambiar a set(list(d1.keys()) + list(d2.keys()))en la respuesta (para Python 3.x)? De lo contrario, arrojará un TypeError: unsupported operand type(s) for +: 'dict_keys' and 'dict_keys'error, en python3.x
R4444

1

Actualización de Python 3.x

De la respuesta de Eli Bendersky:

Python 3 eliminado dict.iteritems usa dict.items en su lugar. Consulte la wiki de Python: https://wiki.python.org/moin/Python3.0

from collections import defaultdict

dd = defaultdict(list)

for d in (d1, d2):
    for key, value in d.items():
        dd[key].append(value)

1

Este método fusiona dos dictados incluso si las claves de los dos diccionarios son diferentes:

def combine_dict(d1, d2):
    combined = {}
    for k in set(d1.keys()) | set(d2.keys()):
        combined[k] = tuple(d[k] for d in [d1, d2] if k in d)
    return combined

Ejemplo:

d1 = {
    'a': 1,
    'b': 2,
}
d2` = {
    'b': 'boat',
    'c': 'car',
}
combine_dict(d1, d2)
# Returns: {
#    'a': (1,),
#    'b': (2, 'boat'),
#    'c': ('car',)
# }

1

Suponga que tiene la lista de TODAS las claves (puede obtener esta lista iterando a través de todos los diccionarios y obtener sus claves). Vamos a nombrarlo listKeys. También:

  • listValues es la lista de TODOS los valores de una única clave que desea fusionar.
  • allDicts: todos los diccionarios que desea fusionar.
result = {}
for k in listKeys:
    listValues = [] #we will convert it to tuple later, if you want.
    for d in allDicts:
       try:
            fileList.append(d[k]) #try to append more values to a single key
        except:
            pass
    if listValues: #if it is not empty
        result[k] = typle(listValues) #convert to tuple, add to new dictionary with key k

0
def merge(d1, d2, merge):
    result = dict(d1)
    for k,v in d2.iteritems():
        if k in result:
            result[k] = merge(result[k], v)
        else:
            result[k] = v
    return result

d1 = {'a': 1, 'b': 2}
d2 = {'a': 1, 'b': 3, 'c': 2}
print merge(d1, d2, lambda x, y:(x,y))

{'a': (1, 1), 'c': 2, 'b': (2, 3)}

0

Para complementar las soluciones de dos listas, aquí hay una solución para procesar una sola lista.

Una lista de muestra (relacionada con NetworkX; formateada manualmente aquí para facilitar la lectura):

ec_num_list = [((src, tgt), ec_num['ec_num']) for src, tgt, ec_num in G.edges(data=True)]

print('\nec_num_list:\n{}'.format(ec_num_list))
ec_num_list:
[((82, 433), '1.1.1.1'),
  ((82, 433), '1.1.1.2'),
  ((22, 182), '1.1.1.27'),
  ((22, 3785), '1.2.4.1'),
  ((22, 36), '6.4.1.1'),
  ((145, 36), '1.1.1.37'),
  ((36, 154), '2.3.3.1'),
  ((36, 154), '2.3.3.8'),
  ((36, 72), '4.1.1.32'),
  ...] 

Tenga en cuenta los valores duplicados para los mismos bordes (definidos por las tuplas). Para cotejar esos "valores" con sus "claves" correspondientes:

from collections import defaultdict
ec_num_collection = defaultdict(list)
for k, v in ec_num_list:
    ec_num_collection[k].append(v)

print('\nec_num_collection:\n{}'.format(ec_num_collection.items()))
ec_num_collection:
[((82, 433), ['1.1.1.1', '1.1.1.2']),   ## << grouped "values"
((22, 182), ['1.1.1.27']),
((22, 3785), ['1.2.4.1']),
((22, 36), ['6.4.1.1']),
((145, 36), ['1.1.1.37']),
((36, 154), ['2.3.3.1', '2.3.3.8']),    ## << grouped "values"
((36, 72), ['4.1.1.32']),
...] 

Si es necesario, convierta esa lista en dict:

ec_num_collection_dict = {k:v for k, v in zip(ec_num_collection, ec_num_collection)}

print('\nec_num_collection_dict:\n{}'.format(dict(ec_num_collection)))
  ec_num_collection_dict:
  {(82, 433): ['1.1.1.1', '1.1.1.2'],
  (22, 182): ['1.1.1.27'],
  (22, 3785): ['1.2.4.1'],
  (22, 36): ['6.4.1.1'],
  (145, 36): ['1.1.1.37'],
  (36, 154): ['2.3.3.1', '2.3.3.8'],
  (36, 72): ['4.1.1.32'],
  ...}

Referencias


0

De blubb respuesta:

También puede formar directamente la tupla usando valores de cada lista

ds = [d1, d2]
d = {}
for k in d1.keys():
  d[k] = (d1[k], d2[k])

Esto podría ser útil si tuvieras un orden específico para tus tuplas.

ds = [d1, d2, d3, d4]
d = {}
for k in d1.keys():
  d[k] = (d3[k], d1[k], d4[k], d2[k]) #if you wanted tuple in order of d3, d1, d4, d2

0

Esta biblioteca me ayudó, tenía una lista de claves anidadas con el mismo nombre pero con diferentes valores, todas las demás soluciones seguían anulando esas claves anidadas.

https://pypi.org/project/deepmerge/

from deepmerge import always_merger

def process_parms(args):
    temp_list = []
    for x in args:
        with open(x, 'r') as stream:
            temp_list.append(yaml.safe_load(stream))

    return always_merger.merge(*temp_list)

0

Si las claves están anidadas:

d1 = { 'key1': { 'nkey1': 'x1' }, 'key2': { 'nkey2': 'y1' } } 
d2 = { 'key1': { 'nkey1': 'x2' }, 'key2': { 'nkey2': 'y2' } }
ds = [d1, d2]
d = {}
for k in d1.keys():
    for k2 in d1[k].keys():
        d.setdefault(k, {})
        d[k].setdefault(k2, [])
        d[k][k2] = tuple(d[k][k2] for d in ds)

rinde:

{'key1': {'nkey1': ('x1', 'x2')}, 'key2': {'nkey2': ('y1', 'y2')}}

-4

Una posibilidad compacta

d1={'a':1,'b':2}
d2={'c':3,'d':4}
context={**d1, **d2}
context
{'b': 2, 'c': 3, 'd': 4, 'a': 1}

la pregunta es sobre la combinación de dictados con la misma clave. tu no es la respuesta requerida.
Pbd
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.