Python newb在这里寻求帮助......
对于python列表中可变数量的dicts,如:
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'},
]
我正在尝试找到将密钥“id”的值相等的分组的最佳方法,然后添加/合并任何唯一的密钥:value并创建一个新的dicts列表,如:
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'},
]
到目前为止,我已经想出如何将列表中的词组分组为:
myList = itertools.groupby(list_dicts, operator.itemgetter('id'))
但是我正在努力构建新的dicts列表:
1)将额外的键和值添加到具有相同“id”
的第一个dict实例中2)设置“item”和“price”键的新名称(例如“item1”,“item2”,“item3”)。这对我来说似乎很笨拙,还有更好的方法吗?
3)遍历每个“id”匹配以构建一个字符串以便稍后输出
我选择返回一个新的dicts列表只是因为将dict传递给模板函数的便利性,其中通过描述性键设置变量是有帮助的(有很多变量)。如果有更简洁的方法来实现这一点,我很想学习。同样,我对Python和处理这样的数据结构都很陌生。
答案 0 :(得分:9)
尽量避免使用复杂的嵌套数据结构。我相信人们倾向于 只有在他们密集使用数据结构时才能使用它们。之后 程序完成,或者搁置一段时间,数据结构很快 变得神秘莫测。
对象可用于以更加有条理的方式保留甚至为数据结构添加丰富性。例如,item
和price
似乎总是在一起。因此,两个数据也可以在一个对象中配对:
class Item(object):
def __init__(self,name,price):
self.name=name
self.price=price
同样,一个人似乎有一个id
和name
以及一组财产:
class Person(object):
def __init__(self,id,name,*items):
self.id=id
self.name=name
self.items=set(items)
如果您接受使用这类课程的想法,那么您的list_dicts
可能会成为
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)),
]
然后,要基于id
合并人员,您可以使用Python的reduce
函数,
与take_items
一起,它接收(合并)一个人的项目并将它们交给另一个人:
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
全部放在一起:
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)
答案 1 :(得分:0)
我想将list_dicts中的项目组合成更像这样的东西会更容易:
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'}]]
您还可以使用“项目”或可能是命名元组的元组列表。
答案 2 :(得分:0)
这看起来非常类似于家庭作业问题。
正如上面提到的海报,这类数据有一些更合适的数据结构,下面的一些变体可能是合理的:
[ ('001', 'jim', [('pencil', '0.99')]),
('002', 'mary', [('book', '15.49'), ('tape', '7.99')]),
('003', 'john', [('pen', '3.49'), ('stapler', '9.49'), ('scissors', '12.99')])]
这可以通过相对简单的方式来实现:
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]))
关于这个问题的一个有趣的事情是在使用groupby时难以提取'name',而没有遍历该项目。
要回到原来的目标,你可以使用这样的代码(如OP建议的那样):
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)