【问题标题】:Deleting dicts with near-duplicate values from a list of dicts - Python从字典列表中删除具有近乎重复值的字典 - Python
【发布时间】:2011-04-14 19:07:43
【问题描述】:

我想清理一个字典列表,按照以下规则:

1) 字典列表已经排序,所以较早的字典优先。
2) 在较低的字典中,如果['name']['code'] 字符串值与列表上任何较高字典的相同键值匹配,并且如果这两个字典之间的int(['cost']) 差异的绝对值是< 2;然后假定该 dict 是先前 dict 的副本,并从列表中删除。

这是字典列表中的一个字典:

{
'name':"ItemName", 
'code':"AAHFGW4S",
'from':"NDLS",
'to':"BCT",
'cost':str(29.95)
 }

这样删除重复项的最佳方法是什么?

【问题讨论】:

    标签: python sorting dictionary


    【解决方案1】:

    可能有更 Pythonic 的方式来做这件事,但这是基本的伪代码:

    def is_duplicate(a,b):
      if a['name'] == b['name'] and a['cost'] == b['cost'] and abs(int(a['cost']-b['cost'])) < 2:
        return True
      return False
    
    newlist = []
    for a in oldlist:
      isdupe = False
      for b in newlist:
        if is_duplicate(a,b):
          isdupe = True
          break
      if not isdupe:
        newlist.append(a)
    

    【讨论】:

    • 虽然有更好的技术方法(尤其是 Jochen 的回答,它使用 yield 来减少大型列表的内存使用),但我更喜欢您方法的可读性。
    【解决方案2】:

    既然你说成本是整数,你可以使用它:

    def neardup( items ):
        forbidden = set()
        for elem in items:
            key = elem['name'], elem['code'], int(elem['cost'])
            if key not in forbidden:
                yield elem
                for diff in (-1,0,1): # add all keys invalidated by this
                    key = elem['name'], elem['code'], int(elem['cost'])-diff
                    forbidden.add(key)
    

    这里有一个不那么棘手的方法来真正计算差异:

    from collections import defaultdict
    def neardup2( items ):
        # this is a mapping `(name, code) -> [cost1, cost2, ... ]`
        forbidden =  defaultdict(list)
        for elem in items:
            key = elem['name'], elem['code']
            curcost = float(elem['cost'])
            # a item is new if we never saw the key before
            if (key not in forbidden or
                  # or if all the known costs differ by more than 2
                  all(abs(cost-curcost) >= 2 for cost in forbidden[key])):
                yield elem
                forbidden[key].append(curcost)
    

    两种解决方案都避免重新扫描每个项目的整个列表。毕竟,只有当(name, code) 相等时,成本才会变得有趣,因此您可以使用字典快速查找所有候选者。

    【讨论】:

    • 感谢您向我介绍yieldset()。你的回答在技术上总是很棒!
    【解决方案3】:

    有点复杂的问题,但我认为这样的事情会起作用:

    for i, d in enumerate(dictList):
        # iterate through the list of dicts, starting with the first
        for k,v in d.iteritems():
            # for each key-value pair in this dict...
            for d2 in dictList[i:]:
                 # check against all of the other dicts "beneath" it
                 # eg,
                 # if d['name'] == d2['name'] and d['code'] == d2['code']:
                 #     --check the cost stuff here--
    

    【讨论】:

    • 谢谢丹尼尔。是的,剪掉列表也是我的第一个本能想法,但我认为 Yasser 使用两个列表的想法最终在一年后重新访问代码时更加清晰。你怎么看?
    猜你喜欢
    • 2022-11-27
    • 2019-01-09
    • 2012-06-22
    • 1970-01-01
    • 2011-10-28
    • 1970-01-01
    • 1970-01-01
    • 2014-01-28
    • 2016-03-13
    相关资源
    最近更新 更多