2010-01-14 17 views
9

Python newb qui alla ricerca di qualche aiuto ...python lista di dit come unire chiave: valore dove i valori sono gli stessi?

Per un numero variabile di dicts in un elenco di pitone come:

list_dicts = [ 
{'id':'001', 'name':'jim', 'item':'pencil', 'price':'0.99'}, 
{'id':'002', 'name':'mary', 'item':'book', 'price':'15.49'}, 
{'id':'002', 'name':'mary', 'item':'tape', 'price':'7.99'}, 
{'id':'003', 'name':'john', 'item':'pen', 'price':'3.49'}, 
{'id':'003', 'name':'john', 'item':'stapler', 'price':'9.49'}, 
{'id':'003', 'name':'john', 'item':'scissors', 'price':'12.99'}, 
] 

Sto cercando di trovare il modo migliore per dicts di gruppo in cui il valore di chiave "id" è uguale, quindi aggiungere/merge qualsiasi unica chiave: valore e creare un nuovo elenco di dicts come:

list_dicts2 = [ 
{'id':'001', 'name':'jim', 'item1':'pencil', 'price1':'0.99'}, 
{'id':'002', 'name':'mary', 'item1':'book', 'price1':'15.49', 'item2':'tape', 'price2':'7.99'}, 
{'id':'003', 'name':'john', 'item1':'pen', 'price1':'3.49', 'item2':'stapler', 'price2':'9.49', 'item3':'scissors', 'price3':'12.99'}, 
] 

Finora, ho capito come raggruppare i dicts nella lista con :

myList = itertools.groupby(list_dicts, operator.itemgetter('id')) 

Ma sto lottando con il modo di costruire il nuovo elenco di dicts a:

1) Aggiungere le chiavi ei valori in più per la prima istanza dict che ha lo stesso "id"

2) Imposta il nuovo nome per le chiavi "item" e "price" (es "item1", "item2", "item3"). Questo mi sembra schifoso, c'è un modo migliore?

3) loop su ogni partita "id" per costruire una stringa per l'uscita successiva

ho scelto di restituire un nuovo elenco di dicts solo a causa della convenienza di passare un dict a una funzione di template in cui l'impostazione di variabili con una chiave descrittiva è utile (ci sono molte vars). Se c'è un modo più pulito e conciso per realizzare questo, sarei curioso di imparare. Ancora una volta, sono abbastanza nuovo in Python e nel lavorare con strutture di dati come questo.

risposta

9

cercare di evitare strutture di dati nidificate complesse. Credo che le persone tendano allo a consumarle solo mentre utilizzano intensamente la struttura dei dati. Dopo che il programma è finito, o viene messo da parte per un po ', la struttura dei dati rapidamente diventa mistificante.

Gli oggetti possono essere utilizzati per conservare o persino aggiungere ricchezza alla struttura dati in modo più ordinato e organizzato. Ad esempio, sembra che lo item e il price vadano sempre insieme. Così i due pezzi di dati potrebbe anche essere accoppiati in un oggetto:

class Item(object): 
    def __init__(self,name,price): 
     self.name=name 
     self.price=price 

Allo stesso modo, una persona sembra avere un id e name e una serie di beni:

class Person(object): 
    def __init__(self,id,name,*items): 
     self.id=id 
     self.name=name 
     self.items=set(items) 

Se si acquista in l'idea di utilizzare le classi come queste, allora la vostra list_dicts potrebbe diventare

list_people = [ 
    Person('001','jim',Item('pencil',0.99)), 
    Person('002','mary',Item('book',15.49)), 
    Person('002','mary',Item('tape',7.99)), 
    Person('003','john',Item('pen',3.49)), 
    Person('003','john',Item('stapler',9.49)), 
    Person('003','john',Item('scissors',12.99)), 
] 

poi, per unire la gente sulla base di id, è possibile utilizzare la funzione di Python reduce, insieme a take_items, che prende (unioni) gli elementi da una persona e li dà ad un altro:

def take_items(person,other): 
    ''' 
    person takes other's items. 
    Note however, that although person may be altered, other remains the same -- 
    other does not lose its items.  
    ''' 
    person.items.update(other.items) 
    return person 

Mettere tutto insieme:

import itertools 
import operator 

class Item(object): 
    def __init__(self,name,price): 
     self.name=name 
     self.price=price 
    def __str__(self): 
     return '{0} {1}'.format(self.name,self.price) 

class Person(object): 
    def __init__(self,id,name,*items): 
     self.id=id 
     self.name=name 
     self.items=set(items) 
    def __str__(self): 
     return '{0} {1}: {2}'.format(self.id,self.name,map(str,self.items)) 

list_people = [ 
    Person('001','jim',Item('pencil',0.99)), 
    Person('002','mary',Item('book',15.49)), 
    Person('002','mary',Item('tape',7.99)), 
    Person('003','john',Item('pen',3.49)), 
    Person('003','john',Item('stapler',9.49)), 
    Person('003','john',Item('scissors',12.99)), 
] 

def take_items(person,other): 
    ''' 
    person takes other's items. 
    Note however, that although person may be altered, other remains the same -- 
    other does not lose its items.  
    ''' 
    person.items.update(other.items) 
    return person 

list_people2 = [reduce(take_items,g) 
       for k,g in itertools.groupby(list_people, lambda person: person.id)] 
for person in list_people2: 
    print(person) 
0

immagino che sarebbe più facile combinare gli elementi in list_dicts in qualcosa che assomiglia di più a questo:

list_dicts2 = [{'id':1, 'name':'jim', 'items':[{'itemname':'pencil','price':'0.99'}], {'id':2, 'name':'mary', 'items':[{'itemname':'book','price':'15.49'}, {'itemname':'tape','price':'7.99'}]]

Si potrebbe anche usare una lista di tuple di 'voci' o forse una tupla di nome .

0

Questo sembra molto simile a un problema di compiti a casa.

Come sopra manifesto detto, ci sono alcune strutture di dati più appropriate per questo tipo di dati, alcune varianti seguente potrebbe essere ragionevole:

[ ('001', 'jim', [('pencil', '0.99')]), 
('002', 'mary', [('book', '15.49'), ('tape', '7.99')]), 
('003', 'john', [('pen', '3.49'), ('stapler', '9.49'), ('scissors', '12.99')])] 

Questo può essere fatto con la relativamente semplice:

list2 = [] 
for id,iter in itertools.groupby(list_dicts,operator.itemgetter('id')): 
    idList = list(iter) 
    list2.append((id,idList[0]['name'],[(z['item'],z['price']) for z in idList])) 

La cosa interessante di questa domanda è la difficoltà nell'estrarre "nome" quando si usa groupby, senza scorrere l'articolo.

Per tornare al l'obiettivo originale, però, è possibile utilizzare il codice come questo (come suggerito il PO):

list3 = [] 
for id,name,itemList in list2: 
    newitem = dict({'id':id,'name':name}) 
    for index,items in enumerate(itemList): 
     newitem['item'+str(index+1)] = items[0] 
     newitem['price'+str(index+1)] = items[1] 
    list3.append(newitem) 
Problemi correlati