python的dicts列表如何合并键:值相同的值?

时间:2010-01-14 21:15:39

标签: python list merge dictionary

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和处理这样的数据结构都很陌生。

3 个答案:

答案 0 :(得分:9)

尽量避免使用复杂的嵌套数据结构。我相信人们倾向于 只有在他们密集使用数据结构时才能使用它们。之后 程序完成,或者搁置一段时间,数据结构很快 变得神秘莫测。

对象可用于以更加有条理的方式保留甚至为数据结构添加丰富性。例如,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)

答案 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)