Python字典列表如何合并相同值的键值对?

10

作为一个新手,我在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个回答

10

尽量避免使用复杂的嵌套数据结构。我认为人们通常只有在密集使用数据结构时才能理解它们。程序完成后或暂时搁置一段时间后,数据结构很快就变得神秘起来。

对象可以以更合理、更有组织的方式保留甚至增加数据结构的丰富性。例如,itemprice似乎总是一起出现。因此,这两个数据元素可以成对地组合成一个对象:

class Item(object):
    def __init__(self,name,price):
        self.name=name
        self.price=price
同样地,一个人似乎有一个idname以及一组财产:
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)

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时提取“名称”的难度,而不需要迭代超过项目。

不过想要回到最初的目标,你可以使用像这样的代码(正如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)

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

您也可以使用元组列表作为'items',或者使用命名元组。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接