2012-02-14 9 views
6

por ejemplo:¿Cuál es la forma más eficiente de la cremallera de dos lista anidada a un diccionario solo nivel

list1=['k1','k2','k3',['k4','k5',['k6','k7']]] 
list2=['v1','v2','v3',['v4','v5',['v6','v7']]] 

y quiero unirlos a un diccionario de esta manera:

dict1={'k1':'v1','k2':'v2','k3':'v3','k4':'v4','k5':'v5','k6':'v6','k7':'v7'} 

tengo una forma de hacerlo, pero creo que toma demasiado tiempo:

def mergeToDict(keyList, valueList): 
    resultDict = {} 
    for key, value in itertools.izip(keyList, valueList): 
     if type(key) == list and type(value) == list: 
      resultDict=dict(resultDict,**mergeToDict(key, value)) 
     elif type(key) != list and type(key) != dict and type(key) != tuple: 
      resultDict[key] = value 
    return resultDict 

¿Hay alguna idea mejor?

+0

Su solución se ve mejor que todas las respuestas a continuación. – jterrace

Respuesta

1

No creo que deba aplanar en absoluto si solo tiene casos de uso como los que presentó (listas anidadas pero con la misma forma). Aquí es un enfoque que, al menos en mi máquina es 2-3 veces más rápido que el suyo (de nuevo sólo funciona con esa restricción):

def appendDict(list1, list2, resultDict): 
    for idx, val in enumerate(list1): 
     if isinstance(val, list):  
      appendDict(val, list2[idx], resultDict) 
     else: 
      resultDict[val] = list2[idx] 

list1=['k1','k2','k3',['k4','k5',['k6','k7']]] 
list2=['v1','v2','v3',['v4','v5',['v6','v7']]] 
resultDict = {} 
appendDict(list1, list2, resultDict) 
print resultDict 

{'k3': 'v3', 'k2': 'v2', 'k1': 'v1', 'k7': 'v7', 'k6': 'v6', 'k5': 'v5', 'k4': 'v4'} 

y una comparación de métodos:

método de OP, en 10000 carreras : 0,290050983429

Otro método propuesto, en 10000 carreras: 0,580717086792

este método, en 10000 carreras: 0,155267000198

Tal vez no tan

elegante como las otras soluciones, pero el rendimiento parecía ser la principal preocupación aquí.

+2

La variable global puede pasarse tan bien como parámetro a la función, la idea es que itere solo en una de las listas y lo haga solo una vez. – Bogdan

+0

Eso es realmente rápido. Tal vez puedas pasar "resultDict" como argumento, que se ve mejor. – BackMountainBird

5

que haría uso de algún tipo de aplanar función:

def flatten(it): 
    if isinstance(it, str): 
     yield it 
     return 
    try: 
     for x in it: 
      for y in flatten(x): 
       yield y 
    except TypeError: 
     yield it 

Ahora usted puede hacer

from itertools import izip 
my_dict = dict(izip(flatten(list1), flatten(list2))) 

Creo que de esta manera es más general y más transparente para el lector.

+0

Probé tu código, pero parece que se ejecuta dos veces más lento que mi código. – BackMountainBird

+3

Hay espacio para la optimización en la función 'flatten()'; por ejemplo, evitar excepciones usando 'isinstance (it, collections.Iterable)' o 'hasattr (it," __iter __ ")', o solo descendiendo a iterables de tipo 'lista'. La mayoría de estos cambios dañarán la generalidad o la legibilidad para obtener un poco de rendimiento. Si el rendimiento es realmente un problema, dudo que este sea el lugar para optimizar, en lugar de cambiar la forma en que estas listas * se crean * en primer lugar. En cualquier caso, * perfil * para asegurarse de que esté optimizando en el lugar correcto. –

+0

Veo tu punto. – BackMountainBird

1

Con flatten define como:

>>> def flatten(l): 
...  r = [] 
...  for x in l: 
...    if isinstance(x, list): 
...      r.extend(flatten(x)) 
...    else: 
...      r.append(x) 
...  return r 

dict(zip(flatten(list1), flatten(list2))) parece ser tan rápido como el suyo. Y es un enfoque mucho más conveniente, como dicen los chicos.

+0

Todavía un poco más lento que mi código ... – BackMountainBird

0

me gusta pilas y funciones del generador:

def flatten(seq, *seq_types): 
    stack = [iter(seq)] 
    while stack: 
     for item in stack[-1]: 
      if isinstance(item, seq_types): 
       stack.append(iter(item)) 
       break 
      else: 
       yield item 
     else: 
      stack.pop() 

keys = [0, 1, (2, 3, [4.])] 
values = (5, 6, (7, "joe", [9])) 
print dict(zip(flatten(keys, list, tuple), flatten(values, tuple, list))) 

Resultado:

{0: 5, 1: 6, 2: 7, 3: 'joe', 4.0: 9} 

O, si se sabe con certeza que las listas de entrada tienen la misma estructura, esto también podría funcionar:

def flatten(seq, *seq_types): 
    seq = list(seq) 
    for item in seq: 
     if isinstance(item, seq_types): 
      seq.extend(item) 
     else: 
      yield item 

Ten en cuenta que el orden de los elementos podría cambiar:

print list(flatten([1, 2, [3, 4, [5]]], list)) 
print list(flatten([1, 2, [[3, 4], 5]], list)) 

Resultado:

[1, 2, 3, 4, 5] 
[1, 2, 5, 3, 4] 
Cuestiones relacionadas