2009-11-22 18 views
9

Cosa devo fare è quello di convertire qualcosa di similedizionario Split di liste nella lista dei dizionari

{'key1': [1, 2, 3], 'key2': [4, 5, 6]} 

in

[{'key1': 1, 'key2': 4}, {'key1': 2, 'key2': 5}, {'key1': 3, 'key2': 6}] 

La lunghezza delle liste valori possono variare! Qual è il modo più veloce per farlo (preferibilmente senza loop)?

+0

Si consiglia di chiarire la tua domanda un po '... Mi c'è voluto del tempo per capire cosa si vuole dal vostro esempio: convertire una mappa con due tasti (key1 , key2) ciascuno con una lista di valori (della stessa lunghezza, ma variabile) per un elenco di coppie, e in coppia alla posizione i, key1 e key2 impostano l'elemento iith della rispettiva lista. È così? –

+0

Perché il fascino di farlo senza cicli? Questo è un limite stupido. – jcdyer

+0

non era un dovere, ho scritto "preferibilmente". Pensato ci potrebbe essere un rapido modo pitonico per farlo (alcune funzioni magiche di cui ancora non sapevo;)) –

risposta

11

funziona per qualsiasi numero di chiavi

>>> map(dict, zip(*[[(k, v) for v in value] for k, value in d.items()])) 
[{'key2': 4, 'key1': 1}, {'key2': 5, 'key1': 2}, {'key2': 6, 'key1': 3}] 

Per esempio :

d = {'key3': [7, 8, 9], 'key2': [4, 5, 6], 'key1': [1, 2, 3]} 

>>> map(dict, zip(*[[(k, v) for v in value] for k, value in d.items()])) 
[{'key3': 7, 'key2': 4, 'key1': 1}, {'key3': 8, 'key2': 5, 'key1': 2}, {'key3': 9, 'key2': 6, 'key1': 3}] 

una soluzione generale che funziona su qualsiasi numero di valori o chiavi: (python2.6)

>>> from itertools import izip_longest 
>>> d = {'key2': [3, 4, 5, 6], 'key1': [1, 2]} 
>>> map(lambda a: dict(filter(None, a)), izip_longest(*[[(k, v) for v in value] for k, value in d.items()])) 
[{'key2': 3, 'key1': 1}, {'key2': 4, 'key1': 2}, {'key2': 5}, {'key2': 6}] 

E se non si dispone di python2.6:

>>> d = {'key2': [3, 4, 5, 6], 'key1': [1, 2]} 
>>> map(lambda a: dict(filter(None, a)), map(None, *[[(k, v) for v in value] for k, value in d.items()])) 
[{'key2': 3, 'key1': 1}, {'key2': 4, 'key1': 2}, {'key2': 5}, {'key2': 6}] 
2

Se ci sono sempre due chiavi è possibile utilizzare:

[{'key1':a, 'key2':b} for (a,b) in zip(d['key1'], d['key2'])] 
1
>>> a = {'key1': [1, 2, 3], 'key2': [4, 5, 6]} 
>>> [dict((key, a[key][i]) for key in a.keys()) for i in range(len(a.values()[0]))] 
[{'key2': 4, 'key1': 1}, {'key2': 5, 'key1': 2}, {'key2': 6, 'key1': 3}] 
+0

la lunghezza della lista valori può variare . range (3) è piuttosto costante. – zlack

1
d = {'key1': [1, 2, 3], 'key2': [4, 5, 6]} 

keys = d.keys() 
vals = zip(*[d[k] for k in keys]) 
l = [dict(zip(keys, v)) for v in vals] 
print l 

produce

[{'key2': 4, 'key1': 1}, {'key2': 5, 'key1': 2}, {'key2': 6, 'key1': 3}] 
5

Supponendo che il numero di chiavi e valori per chiave, sono entrambi arbitraria e a priori sconosciuto, è più semplice ottenere il risultato con i cicli for, ovviamente:

Può essere compresso, ma sono dubbioso circa le implicazioni di prestazione di farlo (se le strutture di dati coinvolte sono così grandi da garantire l'ottimizzazione delle prestazioni, la costruzione di qualsiasi struttura ausiliaria aggiuntiva in memoria oltre ciò che è strettamente necessario potrebbe trasformarsi costoso - questo mio semplice approccio è particolarmente attento a evitare tali strutture intermedie).

: un'altra alternativa, particolarmente interessante se le strutture di dati complessive sono enormi ma in alcuni casi di utilizzo è possibile che siano necessari solo "frammenti" della struttura "trasformata", è di creare una classe che fornisce l'interfaccia hai bisogno, ma lo fa "al volo", piuttosto che in una trasformazione "big bang", "una volta per tutte" (questo potrebbe essere particolarmente utile se la struttura originale può cambiare e quella trasformata deve riflettere lo stato attuale dell'originale, ecc. ecc.).

Ovviamente, per tale scopo è molto utile identificare esattamente quali caratteristiche di un "elenco di dizionari" utilizzarebbe il codice downstream. Supponiamo ad esempio che tutto ciò di cui hai bisogno sia effettivamente indicizzazione di "sola lettura" (non cambiando, iterando, affettando, ordinando, ...): X[x] deve restituire un dizionario in cui ogni chiave k si associa ad un valore tale (caling O dizionario originale delle liste) X[x][k] is O[k][x]. Poi:

class Wrap1(object): 
    def __init__(self, O): 
    self.O = O 
    def __getitem__(self, x): 
    return dict((k, vs[x]) for k, vs in self.O.iteritems()) 

se non in realtà bisogno la struttura avvolta per monitorare le modifiche a quello originale, quindi __getitem__ potrebbe benissimo anche "cache" del dict è il ritorno:

class Wrap2(object): 
    def __init__(self, O): 
    self.O = O 
    self.cache = {} 
    def __getitem__(self, x): 
    r = self.cache.get(x) 
    if r is None: 
     r = self.cache[x] = dict((k, vs[x]) for k, vs in self.O.iteritems()) 
    return r 

Si noti che questo approccio può finire con qualche duplicazione nella cache, ad esempio, se gli elenchi di O hanno ciascuno 7 elementi ciascuno, la cache a x==6 e x==-1 può finire con due dendici uguali; se questo è un problema, ad esempio, è possibile normalizzare i valori negativi s in __getitem__ aggiungendo len(self.O) prima di procedere.

Se è necessaria anche l'iterazione, oltre a questa semplice indicizzazione, non è troppo difficile: basta aggiungere un metodo __iter__, facilmente implementabile ad es. come un semplice generatore ...:

def __iter__(self, x): 
    for i in xrange(len(self.O)): 
     yield self[i] 

E così via, in modo incrementale, se e come avete bisogno sempre di più le funzionalità di un elenco (nel peggiore dei casi, una volta che avete implementato questa __iter__, si può costruire self.L = list(self) - di ritornare al "big bang" approccio - e, per ogni ulteriore richiesta, punt a self.L ... ma dovrai fare uno speciale metaclass se vuoi adottare questo approccio anche per i metodi specialioppure utilizzare il trucco più sottile come self.__class__ = list; self[:] = self.L seguito da appropriato del s ;-).

1
list(map(dict, zip(*([(key, val) for val in data[key]] for key in data.keys())))) 
1

Senza ciclo for, processo interno di MAP è iterare in realtà, solo senza la parola chiave for

>>> x={'key1': [1, 2, 3], 'key2': [4, 5, 6]} 

>>> map(lambda x,y:{'key1':x,'key2':y},x['key1'],x['key2']) 

[{'key2': 4, 'key1': 1}, {'key2': 5, 'key1': 2}, {'key2': 6, 'key1': 3}] 
0

Che ne dici?

d = {'key1': [1, 2, 3], 'key2': [4, 5, 6]} 
[dict(zip(d.keys(),i)) for i in zip(*d.values())] 

Returns:

[{'key1': 1, 'key2': 4}, {'key1': 2, 'key2': 5}, {'key1': 3, 'key2': 6}] 
Problemi correlati