【问题标题】:How to remove duplicate dictionaries from a list in Python?如何从 Python 列表中删除重复的字典?
【发布时间】:2013-09-01 02:54:30
【问题描述】:

我有一个按特定键排序的字典列表。每个字典包含 32 个元素,列表中有超过 4000 个字典。我需要代码来处理列表并返回一个删除所有重复项的新列表。

这些链接中的方法:

不要帮助我,因为字典是不可散列的。

有什么想法吗?如果您需要更多信息,请发表评论,我会添加信息。

编辑

重复字典可以是具有相同 list[dictionary][key] 值的任意两个字典。


好的,这里给需要的人详细解释一下。

我有一个这样的字典列表:

[ {
    "ID" : "0001",
    "Organization" : "SolarUSA",
    "Matchcode" : "SolarUSA, Something Street, Somewhere State, Whatev Zip",
    "Owner" : "Timothy Black",
   }, {
    "ID" : "0002",
    "Organization" : "SolarUSA",
    "Matchcode" : "SolarUSA, Something Street, Somewhere State, Whatev Zip",
    "Owner" : "Johen Wilheim",
   }, {
    "ID" : "0003",
    "Organization" : "Zapotec",
    "Matchcode" : "Zapotec, Something Street, Somewhere State, Whatev Zip",
    "Owner" : "Simeon Yurrigan",
   } ]

在这个列表中,第一个和第二个字典是重复的,因为它们的Matchcodes 是相同的。

现在这个列表按以下代码排序:

# sort_by is "Matchcode"
def sort( list_to_be_sorted, sort_by ):
    return sorted(list_to_be_sorted, key=lambda k: k[sort_by])

所以我有一个按Matchcode 排序的字典列表。现在我只需要遍历列表,访问list[dictionary][key] 并在两个键值匹配时删除重复项。

【问题讨论】:

  • “任何与key具有相同值的字典”是什么意思?您的意思是每个键的值都相同? key 的值与您在外部指定的相同?
  • 看起来您将字典用作穷人的对象。 (这没有错。)这个问题可能暗示您应该为这些数据创建一个类。然后您可以定义__eq____hash__ 以及其他适用的答案。
  • 你能发布一个例子,比如说,其中三个字典,其中两个应该被认为是重复的吗?目前还不清楚字典被视为重复的标准是什么。
  • @MikeGraham:是的,在这种情况下它们将是重复的,因为键的值(在这种情况下为“3”)是相同的。
  • @Jacob-IT:这正是我们要求SSCCE 的原因。很明显,字典有 32 个键这一事实与您的问题无关;如果他们只有 3 个钥匙,那将是同样的问题,对吧?所以,给我们一个精简到 3 个字典和 3 个键的版本,它的文本比你现在拥有的要少得多,而且更清晰。

标签: python list python-2.7 dictionary duplicates


【解决方案1】:

基本上你需要像no_dup(checked_val_extrator, list) 这样的东西,其中 no_dup 可能看起来像:

def no_dup(extractor, lst):
   "keeps only first elements encountered for any particular extracted value using =="
   known = set()
   res = []
   for item in lst:
     if extractor(item) in known: continue
     known.add(extractor(item))
     res.append(item)
   return res

【讨论】:

    【解决方案2】:

    使用itertools.groupby() 按键值对字典进行分组,然后从每个组中取出第一项。

    import itertools
    
    data =[ {
        "ID" : "0001",
        "Organization" : "SolarUSA",
        "Matchcode" : "SolarUSA, Something Street, Somewhere State, Whatev Zip",
        "Owner" : "Timothy Black",
       }, {
        "ID" : "0002",
        "Organization" : "SolarUSA",
        "Matchcode" : "SolarUSA, Something Street, Somewhere State, Whatev Zip",
        "Owner" : "Johen Wilheim",
       }, {
        "ID" : "0003",
        "Organization" : "Zapotec",
        "Matchcode" : "Zapotec, Something Street, Somewhere State, Whatev Zip",
        "Owner" : "Simeon Yurrigan",
       } ]
    
    
    print [g.next() for k,g in itertools.groupby(data, lambda x: x['Matchcode'])]
    

    给出结果

    [{'Owner': 'Timothy Black',  
      'Organization': 'SolarUSA', 
      'ID': '0001',  
      'Matchcode': 'SolarUSA, Something Street, Somewhere State, Whatev Zip'},
    
     {'Owner': 'Simeon Yurrigan', 
      'Organization': 'Zapotec', 
      'ID': '0003', 
      'Matchcode':'Zapotec, Something Street, Somewhere State, Whatev Zip'}]
    

    我相信这就是你要找的。​​p>

    编辑:我更喜欢 unique_justseen 解决方案。它更短,更具描述性。

    【讨论】:

    • 如果有人收到属性错误,只需更改 >>> print [g.next() for k,g in itertools.groupby(data, lambda x: x['Matchcode'])] 到这个>>> print [next(g) for k,g in itertools.groupby(data, lambda x: x['Matchcode'])]
    【解决方案3】:

    现在我们可以看到,如果特定键匹配,则两个字典是重复的,问题非常简单。只需遍历字典;跟踪您看到的键,最后从唯一的键中创建一个新列表。

    import collections
    def get_unique_items(list_of_dicts, key="Matchcode"):
        # Count how many times each key occurs.
        key_count = collections.defaultdict(lambda: 0)
        for d in list_of_dicts:
            key_count[d[key]] += 1
    
        # Now return a list of only those dicts with a unique key.
        return [d for d in list_of_dicts if key_count[d[key]] == 1]
    

    请注意,我在这里使用defaultdict 来计算每个键的出现次数(还有其他方法可以做到这一点,但我个人认为这是最干净的)。我没有使用set 来跟踪“已访问”键的原因是您将获得列表中每个键的副本,包括重复的键。这意味着您必须保留 set,以跟踪真正重复的键(遇到它们时已经在“已访问”键中的键),因此您不会包括他们。

    另一方面,如果您只想使用每个给定键获取您看到的第一个字典,无论以后是否存在重复,set 方法都可以正常工作,就像在Mike Graham's second answer 中一样。

    【讨论】:

      【解决方案4】:

      所以我有一个按 Matchcode 排序的整洁的字典列表。现在我只需要遍历列表,访问 list[dictionary][key] 并在两个键值匹配时删除重复项。

      我仍然不完全确定这意味着什么。听起来您是在说它们将始终按您要用于唯一化的相同键进行排序。如果是这样,您可以使用itertools recipes 中的unique_justseen,使用与sort 中相同的键功能,例如itemgetter(key)

      使用您编辑的问题中的示例list_of_dicts

      >>> list(unique_justseen(list_of_dicts, key=itemgetter('Matchcode')))
      [{'ID': '0001',
        'Matchcode': 'SolarUSA, Something Street, Somewhere State, Whatev Zip',
        'Organization': 'SolarUSA',
        'Owner': 'Timothy Black'},
       {'ID': '0003',
        'Matchcode': 'Zapotec, Something Street, Somewhere State, Whatev Zip',
        'Organization': 'Zapotec',
        'Owner': 'Simeon Yurrigan'}]
      

      如果它们是按 不同 键排序的,那么它们被排序的事实根本不相关,unique_justseen 不会工作:

      >>> list_of_dicts.sort(key=itemgetter('Owner'))
      >>> list(unique_justseen(list_of_dicts, key=itemgetter('Matchcode')))
      [{'ID': '0002',
        'Matchcode': 'SolarUSA, Something Street, Somewhere State, Whatev Zip',
        'Organization': 'SolarUSA',
        'Owner': 'Johen Wilheim'},
       {'ID': '0003',
        'Matchcode': 'Zapotec, Something Street, Somewhere State, Whatev Zip',
        'Organization': 'Zapotec',
        'Owner': 'Simeon Yurrigan'},
       {'ID': '0001',
        'Matchcode': 'SolarUSA, Something Street, Somewhere State, Whatev Zip',
        'Organization': 'SolarUSA',
        'Owner': 'Timothy Black'}]
      

      但是你只需要改用unique_everseen 配方:

      >>> list_of_dicts.sort(key=itemgetter('Owner'))
      >>> list(unique_everseen(list_of_dicts, key=itemgetter('Matchcode')))
      [{'ID': '0002',
        'Matchcode': 'SolarUSA, Something Street, Somewhere State, Whatev Zip',
        'Organization': 'SolarUSA',
        'Owner': 'Johen Wilheim'},
       {'ID': '0003',
        'Matchcode': 'Zapotec, Something Street, Somewhere State, Whatev Zip',
        'Organization': 'Zapotec',
        'Owner': 'Simeon Yurrigan'}]
      

      (当然这次我们得到的是 0002 而不是 0001,因为在对 Owner 进行排序之后,它现在是 Matchcode 的第一个值,而不是第二个。)


      字典不可散列的事实与此无关,因为配方只是将键函数的结果存储在它们的集合中,所以只要存储在键 key 的值是可散列的,一切都是很好。

      【讨论】:

        【解决方案5】:

        这个答案对于现在已经消除歧义的问题是不正确的。


        【讨论】:

        • 但是这段代码不是从字典中返回一个键列表吗?我需要一个脚本来返回一个 dicts 列表,只是缺少重复项。
        • @Jacob-IT,unique_values 是一个可迭代的已被唯一化的字典(其中“重复”被视为相等的字典)。
        • @StevenRumbalski,你能解释一下你在说什么吗?
        • @StevenRumbalski,我认为您根本不了解发布的代码的作用。 unique_everseen 在给定一个可迭代的 dicts 的情况下返回一个可迭代的 dicts(list_of_dicts 是 OP 的 dicts 列表)。我不知道你为什么认为它会返回“只是键”或“字典值的元组”或其他任何东西。
        • 你的as_values函数就是operator.itemgetter(*the_keys)
        【解决方案6】:
        seen_values = set()
        without_duplicates = []
        for d in list_of_dicts:
            value = d[key]
            if value not in seen_values:
                without_duplicates.append(d)
                seen_values.add(value)
        

        【讨论】:

          【解决方案7】:

          正如您可以使用tuple 获得与list 等效的哈希值一样,您也可以使用frozenset 获得与dict 等效的哈希值。唯一的技巧是您需要将d.items() 而不是d 传递给构造函数。

          >>> d = {'a': 1, 'b': 2}
          >>> s = frozenset(d.items())
          >>> hash(s)
          -7588994739874264648
          >>> dict(s) == d
          True
          

          然后您就可以使用您已经看过的最喜欢的解决方案。将它们转储到set,或者如果您需要保留订单等,请使用OrderedSetunique_everseen 配方等。例如:

          >>> unique_sets = set(frozenset(d.items()) for d in list_of_dicts)
          >>> unique_dicts = [dict(s) for s in unique_sets]
          

          或者,保持顺序并使用键值:

          >>> sets = (frozenset(d.items()) for d in list_of_dicts)
          >>> unique_sets = unique_everseen(sets, key=operator.itemgetter(key))
          >>> unique_dicts = [dict(s) for s in unique_sets]
          

          当然,如果您有嵌套的列表或字典,您将不得不递归转换,就像您对列表列表所做的那样。

          【讨论】:

          • unique_everseen 期望 key 是一个函数。 >>> unique_sets = unique_everseen(sets, key=operator.itemgetter)
          【解决方案8】:

          我不是 100% 清楚你想要达到的目标,但是:

          删除所有重复的字典条目

          只要你不介意合并所有的字典,

          import itertools
          dict(itertools.chain(*map(lambda x: x.items(), list_of_dictionaries)))
          

          【讨论】:

          • (一般来说,如果你的代码使用eval,你就错了。;)
          • 感谢您的示例,迈克-我已纠正。至于eval,我并不完全同意你的看法。 eval 对生产来说很糟糕,而且通常不是好的做法,但对于孤立的内部一次性数据处理黑客来说,它通常很好。
          猜你喜欢
          • 2023-01-08
          • 2011-10-28
          • 1970-01-01
          • 2019-01-09
          • 2022-11-27
          • 2014-01-28
          • 2016-03-14
          • 1970-01-01
          • 2012-06-22
          相关资源
          最近更新 更多