【问题标题】:python list of dicts how to merge key:value where values are same?python list of dicts 如何在值相同的地方合并键:值?
【发布时间】:2011-01-05 06:35:06
【问题描述】:

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”的值相等的字典进行分组的最佳方法,然后添加/合并任何唯一的 key:value 并创建一个新的字典列表,例如:

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”的第一个 dict 实例

2) 为“item”和“price”键设置新名称(例如“item1”、“item2”、“item3”)。这对我来说似乎很笨拙,有没有更好的方法?

3) 循环遍历每个“id”匹配以构建一个字符串供以后输出

我选择返回一个新的字典列表只是因为将字典传递给模板函数很方便,在该函数中通过描述性键设置变量很有帮助(有很多变量)。如果有更简洁更简洁的方法来实现这一点,我会很想学习。再说一次,我对 Python 还是很陌生,并且在使用这样的数据结构。

【问题讨论】:

    标签: python list merge dictionary


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

      我想将 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'}]]

      您还可以将元组列表用于“项目”或命名元组。

      【讨论】:

        【解决方案3】:

        这看起来很像一个家庭作业问题。

        正如上面的海报所提到的,对于这种数据有一些更合适的数据结构,下面的一些变体可能是合理的:

        [ ('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)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-08-28
          • 2017-04-24
          • 2020-11-12
          • 1970-01-01
          • 1970-01-01
          • 2015-09-22
          • 2019-01-22
          • 1970-01-01
          相关资源
          最近更新 更多