如何合并值相同的Python字典列表中的key:value?
我是一名Python新手,想寻求一些帮助...
我有一个包含多个字典的Python列表,像这样:
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”值相同的字典分组,然后把任何独特的键值对合并起来,创建一个新的字典列表,像这样:
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'))
但是我在构建新的字典列表时遇到了困难,具体来说:
1) 我想把额外的键值对添加到第一个“id”相同的字典中。
2) 我想给“item”和“price”这两个键设置新的名称(比如“item1”、“item2”、“item3”)。我觉得这样做有点笨拙,有没有更好的方法?
3) 我想遍历每个“id”的匹配项,以便构建一个字符串,方便后续输出。
我选择返回一个新的字典列表,主要是因为这样在传递给模板函数时,使用描述性键来设置变量会更方便(变量很多)。如果有更简洁的方法来实现这一点,我很想了解一下。再次强调,我对Python还很陌生,尤其是在处理这样的数据结构时。
3 个回答
我想把 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'}]]
你也可以用一个元组的列表来表示 'items'
,或者用一个命名元组。
这看起来很像是个作业题。
正如上面提到的,有一些更合适的数据结构来处理这种数据,下面的几种变体可能会比较合理:
[ ('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' 会比较困难,因为你不能直接跳过这个项目。
不过,回到最初的目标,你可以使用这样的代码(正如提问者所建议的):
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)
尽量避免使用复杂的嵌套数据结构。我觉得人们通常只有在频繁使用这些数据结构时才能理解它们。一旦程序完成,或者放置一段时间后,这些数据结构就会变得让人摸不着头脑。
对象可以用来更好地组织和丰富数据结构。例如,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)