2012-02-14 10 views
6

ad esempio:Qual è il modo più efficace per comprimere due lista annidata ad un unico dizionario livello

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

e voglio unirli a un dizionario come questo:

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

Ho un modo per farlo, ma penso che ci vuole troppo tempo:

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 

C'è qualche idea migliore?

+0

La soluzione sembra migliore rispetto a tutte le risposte qui sotto. – jterrace

risposta

1

Non penso che sia necessario appiattire del tutto se si hanno solo casi d'uso come quelli che hai presentato (elenchi annidati ma con la stessa forma). Qui è un approccio che, almeno sulla mia macchina è di 2-3 volte più veloce di vostro (di nuovo funziona solo con quel vincolo):

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'} 

E un confronto tra metodi:

il metodo di OP, su 10000 piste : 0,290050983429

Altro metodo proposto, su 10000 piste: 0,580717086792

questo metodo, su 10000 piste: 0,155267000198

Forse non

come elegante come le altre soluzioni, ma le prestazioni sembravano essere la preoccupazione principale qui.

+2

La variabile globale può essere ugualmente passata come parametro alla funzione, l'idea è che itera solo su uno degli elenchi e lo fa solo una volta. – Bogdan

+0

È molto veloce. Forse puoi passare in "resultDict" come argomento, che sembra migliore. – BackMountainBird

5

userei una sorta di appiattire funzione:

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 

ora si può fare

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

Penso che in questo modo è più generale e più trasparente per il lettore.

+0

Ho testato il codice, ma sembra che funzioni due volte più lentamente del mio codice. – BackMountainBird

+3

C'è spazio per l'ottimizzazione nella funzione 'flatten()' - per esempio evitando eccezioni usando 'isinstance (it, collections.Iterable)' o 'hasattr (it," __iter __ ")', o solo decrescente in iterabili di tipo 'list'. La maggior parte di questi cambiamenti danneggerà la generalità o la leggibilità per ottenere un minimo di prestazioni. Se le prestazioni sono davvero un problema, dubito che questo sia il posto giusto per ottimizzare, piuttosto cambiare il modo in cui questi elenchi sono * creati * in primo luogo. In ogni caso, * profilo * per assicurarti che la tua ottimizzazione sia nel posto giusto. –

+0

Vedo il tuo punto. – BackMountainBird

1

Con flatten definito come:

>>> 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))) sembra essere veloce come la tua. Ed è un approccio molto più conveniente, come hanno detto i ragazzi.

+0

Ancora un po 'più lento del mio codice .. – BackMountainBird

0

mi piace pile e le funzioni del generatore:

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))) 

Risultato:

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

Oppure, se si sa per certo che le liste di ingresso hanno la stessa struttura, questo potrebbe anche funzionare:

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

Attenzione che l'ordine degli articoli potrebbe cambiare:

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

Risultato:

[1, 2, 3, 4, 5] 
[1, 2, 5, 3, 4] 
Problemi correlati