2009-11-22 13 views
9

lo que tengo que hacer es convertir algo como estodiccionario división de las listas en la lista de diccionarios

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

en

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

La longitud de las listas de valores pueden variar! ¿Cuál es la forma más rápida de hacerlo (preferiblemente sin bucles for)?

+0

Es posible que desee para aclarar su pregunta un poco ... Me llevó algún tiempo para entender lo que quiere de su ejemplo: convertir un mapa con dos llaves (key1 , tecla2) cada uno con una lista de valores (del mismo, pero de longitud variable) a una lista a parejas, y en pareja en la posición i, tecla1 y tecla2 configurados al i-ésimo elemento de su respectiva lista. ¿Es asi? –

+0

¿Por qué la fascinación por hacerlo sin los bucles? Esa es una restricción estúpida. – jcdyer

+0

no era obligatorio, escribí "preferiblemente". Pensé que podría haber una manera pitónica rápida de hacer esto (alguna función mágica que no sabía aún;)) –

Respuesta

11

funciona para cualquier número de teclas

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

Por ejemplo :

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 solución general que funciona en cualquier número de valores o claves: (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}] 

Y si usted no tiene 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

Si siempre hay dos teclas que puede utilizar:

[{'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 longitud de la lista de valores puede variar . rango (3) es bastante constante. – 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

Suponiendo que el número de claves y valores por clave, son a la vez arbitraria y a priori desconocido, es más simple obtener el resultado con bucles for, por supuesto:

itit = thedict.iteritems() 
    k, vs = next(itit) 
    result = [{k: v} for v in vs] 
    for k, vs in itit: 
    for d, v in itertools.izip(result, vs): 
     d[k] = v 

Se puede plegarse, pero estoy dudoso acerca de las implicaciones de rendimiento de hacerlo (si las estructuras de datos involucrados son tan grandes como para justificar la optimización del rendimiento, la construcción de cualquier estructura auxiliar adicional en la memoria más allá de lo estrictamente necesario puede girar fuera costoso: este enfoque simple mío es especialmente cuidadoso para evitar cualquier estructura intermedia de este tipo).

Editar: otra alternativa, particularmente interesante si las estructuras de datos globales son enormes pero en algunos casos de uso es posible que solo necesites "partes y piezas" de la estructura "transformada" para construir una clase que proporcione la interfaz lo requiere, pero lo hace "sobre la marcha", en lugar de hacerlo en una transformación de "gran explosión", "de una vez por todas" (esto podría ser especialmente útil si la estructura original puede cambiar y la transformada debe reflejar el estado actual del original, etc., etc.).

Por supuesto, para tal fin, es muy útil identificar exactamente qué características de una "lista de diccionarios" usaría su código de flujo descendente. Supongamos, por ejemplo, que todo lo que necesita es indexación de "solo lectura" (sin cambiar, iterar, dividir, ordenar, ...): X[x] debe devolver un diccionario en el que cada tecla k se corresponda con un valor tal que (calificando O diccionario original de listas) X[x][k] is O[k][x]. Entonces:

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

Si no, de hecho, necesitan la estructura envuelta para rastrear las modificaciones a la original, a continuación, __getitem__ bien podría también "caché" de la dict está volviendo:

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 

Tenga en cuenta que este enfoque puede terminar con alguna duplicación en la memoria caché, por ejemplo, si las listas de O tienen 7 elementos cada una, la memoria caché en x==6 y x==-1 puede terminar con dos dictos iguales; si eso es un problema, puede, por ejemplo, normalizar x negativo en __getitem__ agregándole len(self.O) antes de continuar.

Si también necesita iteración, así como esta simple indexación, no es demasiado difícil: simplemente agregue un método __iter__, fácil de implementar, p. como un simple generador ...:

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

Y así sucesivamente, de forma incremental, si hubiere y que necesita cada vez más de la funcionalidad de una lista (en el peor, una vez que haya implementado este __iter__, se puede construir self.L = list(self) - volviendo a la "gran explosión" enfoque - y, para cualquier otra solicitud, punt a self.L ... pero tendrás que hacer una metaclase especial si quieres tomar ese enfoque para los métodos especiales también, o usar algún truco más sutil como self.__class__ = list; self[:] = self.L seguido por el del s apropiado ;-).

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

Sin bucle for, proceso interno de mapa produce la iteración en realidad, solo que sin la palabra clave 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

¿Qué tal?

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

Devuelve:

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