2012-01-03 10 views
7

Busco la genérica forma más sencilla de convertir esta lista Python:forma genérica para crear el diccionario de anidado de la lista plana en Python

x = [ 
     {"foo":"A", "bar":"R", "baz":"X"}, 
     {"foo":"A", "bar":"R", "baz":"Y"}, 
     {"foo":"B", "bar":"S", "baz":"X"}, 
     {"foo":"A", "bar":"S", "baz":"Y"}, 
     {"foo":"C", "bar":"R", "baz":"Y"}, 
    ] 

en:

foos = [ 
     {"foo":"A", "bars":[ 
           {"bar":"R", "bazs":[ {"baz":"X"},{"baz":"Y"} ] }, 
           {"bar":"S", "bazs":[ {"baz":"Y"} ] }, 
          ] 
     }, 
     {"foo":"B", "bars":[ 
           {"bar":"S", "bazs":[ {"baz":"X"} ] }, 
          ] 
     }, 
     {"foo":"C", "bars":[ 
           {"bar":"R", "bazs":[ {"baz":"Y"} ] }, 
          ] 
     }, 
     ] 

La combinación "foo "," bar "," baz "es único, y como puede ver, la lista no está necesariamente ordenada por esta clave.

+6

¿Cuál es su (no necesariamente la más simple, sino la suya) forma de hacerlo? – eumiro

Respuesta

3
#!/usr/bin/env python3 
from itertools import groupby 
from pprint import pprint 

x = [ 
     {"foo":"A", "bar":"R", "baz":"X"}, 
     {"foo":"A", "bar":"R", "baz":"Y"}, 
     {"foo":"B", "bar":"S", "baz":"X"}, 
     {"foo":"A", "bar":"S", "baz":"Y"}, 
     {"foo":"C", "bar":"R", "baz":"Y"}, 
    ] 


def fun(x, l): 
    ks = ['foo', 'bar', 'baz'] 
    kn = ks[l] 
    kk = lambda i:i[kn] 
    for k,g in groupby(sorted(x, key=kk), key=kk): 
     kg = [dict((k,v) for k,v in i.items() if k!=kn) for i in g] 
     d = {} 
     d[kn] = k 
     if l<len(ks)-1: 
      d[ks[l+1]+'s'] = list(fun(kg, l+1)) 
     yield d 

pprint(list(fun(x, 0))) 

[{'bars': [{'bar': 'R', 'bazs': [{'baz': 'X'}, {'baz': 'Y'}]}, 
      {'bar': 'S', 'bazs': [{'baz': 'Y'}]}], 
    'foo': 'A'}, 
{'bars': [{'bar': 'S', 'bazs': [{'baz': 'X'}]}], 'foo': 'B'}, 
{'bars': [{'bar': 'R', 'bazs': [{'baz': 'Y'}]}], 'foo': 'C'}] 

nota: dict es desordenada! pero es lo mismo que el tuyo

0

Me gustaría definir una función que lleva a cabo una única etapa de agrupación como esto:

from itertools import groupby 
def group(items, key, subs_name): 
    return [{ 
     key: g, 
     subs_name: [dict((k, v) for k, v in s.iteritems() if k != key) 
      for s in sub] 
    } for g, sub in groupby(sorted(items, key=lambda item: item[key]), 
     lambda item: item[key])] 

y luego hacer

[{'foo': g['foo'], 'bars': group(g['bars'], "bar", "bazs")} for g in group(x, 
    "foo", "bars")] 

que da el resultado deseado para foos.

0

Este es un simple bucle sobre los datos, sin recursividad. Un árbol auxiliar donde los valores son claves de diccionario se utiliza como un índice para el árbol de resultados mientras se está construyendo.

def make_tree(diclist, keylist): 
    indexroot = {} 
    root = {} 
    for d in diclist: 
     walk = indexroot 
     parent = root 
     for k in keylist: 
      walk = walk.setdefault(d[k], {}) 
      node = walk.setdefault('node', {}) 
      if not node: 
       node[k] = d[k] 
       parent.setdefault(k+'s',[]).append(node) 
      walk = walk.setdefault('children', {}) 
      parent = node 
    return root[keylist[0]+'s'] 

foos = make_tree(x, ["foo","bar","baz"]) 
Cuestiones relacionadas