2010-01-12 14 views
27

Estoy pasando por un montón de tuplas con una correlación de muchos a muchos, y quiero hacer un diccionario donde cada b de (a, b) tenga una lista de todas las a que correspondan a una b. Parece incómodo probar una lista en la clave b en el diccionario, luego buscar una a, luego agregar una si no está allí, cada vez a través del ciclo de digestión de la tupla; pero no he encontrado una mejor manera todavía. ¿Existe uno? ¿Hay alguna otra forma de hacer esto que sea mucho más bonita?¿Existe una manera eficiente de crear una lista o anexarla si ya existe?

+1

por más bonito que quiere decir sintácticamente o algorítmicamente? –

Respuesta

36

Ver the docs para el método setdefault():

setdefault (tecla [, por defecto])
Si la clave es en el diccionario, devolver su valor. De lo contrario, inserte la clave con un valor de predeterminado y devuelva el valor predeterminado. el valor predeterminado está predeterminado en Ninguno.

Usted puede utilizar esto como una única llamada que hará que b si existe, o la serie B a una lista vacía si no existe - y de cualquier manera, el retorno b:

>>> key = 'b' 
>>> val = 'a' 
>>> print d 
{} 
>>> d.setdefault(key, []).append(val) 
>>> print d 
{'b': ['a']} 
>>> d.setdefault(key, []).append('zee') 
>>> print d 
{'b': ['a', 'zee']} 

Combine esto con un simple "no en" comprobar y que ha hecho lo que está buscando en tres líneas:

>>> b = d.setdefault('b', []) 
>>> if val not in b: 
... b.append(val) 
... 
>>> print d 
{'b': ['a', 'zee', 'c']} 
+3

'defaultdict' es un poco mejor que' setdefault', suponiendo que tiene Python 2.5 o superior. – ephemient

+1

Estoy atrapado con 2.34, así que esta es en realidad la respuesta, para mí, ¡gracias, James! – user249228

+5

D'oh. 'set()' es bueno, pero no está incorporado hasta 2.4. ¿Por qué tu Python es tan viejo? :-( – ephemient

2

puede ordenar su tuplas O (n log n) a continuación, crear su propio diccionario O (n)

o simplier O (n), pero podría imponer una carga pesada sobre la memoria en caso de muchas tuplas:

your_dict = {} 
for (a,b) in your_list: 
    if b in your_dict: 
     your_dict[b].append(a) 
    else: 
     your_dict[b]=[a] 

Hmm es más o menos lo mismo que usted ha descrito. ¿Qué tiene de incómodo?

También podría considerar el uso de una base de datos sql para hacer el trabajo sucio.

+0

El método más simple es O (n), por cierto, por lo que es preferible ordenar el método de tuplas. – kennytm

+0

sí, también lo indiqué en la versión editada. –

+0

algún comentario sobre downvoting? –

0

No estoy seguro de cómo va a salir de la prueba clave, pero una vez que par clave/valor se ha inicializado es fácil :)

d = {} 
if 'b' not in d: 
    d['b'] = set() 
d['b'].add('a') 

El conjunto se asegurará de que sólo 1 de 'una 'está en la colección. Sin embargo, debe hacer la comprobación inicial 'b' para asegurarse de que exista la clave/valor.

+0

curiosidad por qué -1? ¿Está esto mal de alguna manera? Eliminaré la respuesta si está mal. –

15

Suponiendo que en realidad no estás atado a las listas, y defaultdictset son bastante práctico.

import collections 
d = collections.defaultdict(set) 
for a, b in mappings: 
    d[b].add(a) 

Si realmente desea listas en lugar de conjuntos, se puede seguir esto con un

for k, v in d.iteritems(): 
    d[k] = list(v) 

Y si realmente desea un diccionario en lugar de un defaultdict, se puede decir

d = dict(d) 

Aunque no veo ninguna razón por la que lo desee.

+0

ah sí, esto pasa por la comprobación inicial sin ningún valor. Gracias! Aprendí algo nuevo. :) –

+1

+1 por 'defaultdict', porque realmente es la solución más Pythonic. – jathanism

+1

También me gustó cómo [este tipo me ayudó a encontrar el default (lambda: defaultdict (list))] (http://ohuiginn.net/mt/2010/07/nested_dictionaries_in_python.html) – lkraav

4

Usar colecciones.defaultdict

your_dict = defaultdict(list) 
for (a,b) in your_list: 
    your_dict[b].append(a) 
+0

¿No es así? 'append'? – interjay

+0

Sí, lo dije en serio. Gracias –

+0

OP "a continuación, agregue un si no está ya allí" me hace pensar que la lista original puede tener duplicados que deben filtrarse, por lo que utilicé 'set' en lugar de' list'. – ephemient

3

lugar de utilizar un if, que yo sepa, es más Pythonic utilizar un bloque try lugar.

your_list=[('a',1),('a',3),('b',1),('f',1),('a',2),('z',1)] 

your_dict={} 
for (a,b) in your_list: 
    try: 
     your_dict[b].append(a) 
    except KeyError: 
     your_dict[b]=[a] 

print your_dict 
0

Dict get method? Devuelve el valor de my_dict[some_key] si some_key está en el diccionario, y si no - devuelve algún valor por defecto ([] en el ejemplo siguiente):

my_dict[some_key] = my_dict.get(some_key, []).append(something_else) 
0

Hay otra manera que sea más eficiente (aunque quizás no tan eficiente como conjuntos) y simple. Es similar en la práctica al defaultdict pero no requiere una importación adicional. Dado que tienes un dict con teclas vacías (None), significa que también creas las claves dict en algún lado. Puede hacerlo con el método dict.fromkeys, y este método también permite establecer un valor predeterminado para todas las teclas.

keylist = ['key1', 'key2'] 
result = dict.fromkeys(keylist, []) 

donde result habrá: { 'key1': [], 'clave2': []}

entonces usted puede hacer su bucle y utilizar result['key1'].append(..) directamente

Cuestiones relacionadas