【问题标题】:List of unique dictionariesPython - 唯一字典列表
【发布时间】:2012-06-20 23:50:14
【问题描述】:

假设我有一个字典列表:

[
    {'id': 1, 'name': 'john', 'age': 34},
    {'id': 1, 'name': 'john', 'age': 34},
    {'id': 2, 'name': 'hanna', 'age': 30},
]

我需要获取唯一字典列表(删除重复项):

[
    {'id': 1, 'name': 'john', 'age': 34},
    {'id': 2, 'name': 'hanna', 'age': 30},
]

谁能帮助我以最有效的方式在 Python 中实现这一目标?

【问题讨论】:

标签: python dictionary


【解决方案1】:

所以用id 的键创建一个临时字典。这会过滤掉重复项。 dict 的values() 将是列表

在 Python2.7 中

>>> L=[
... {'id':1,'name':'john', 'age':34},
... {'id':1,'name':'john', 'age':34},
... {'id':2,'name':'hanna', 'age':30},
... ]
>>> {v['id']:v for v in L}.values()
[{'age': 34, 'id': 1, 'name': 'john'}, {'age': 30, 'id': 2, 'name': 'hanna'}]

在 Python3 中

>>> L=[
... {'id':1,'name':'john', 'age':34},
... {'id':1,'name':'john', 'age':34},
... {'id':2,'name':'hanna', 'age':30},
... ] 
>>> list({v['id']:v for v in L}.values())
[{'age': 34, 'id': 1, 'name': 'john'}, {'age': 30, 'id': 2, 'name': 'hanna'}]

在 Python2.5/2.6 中

>>> L=[
... {'id':1,'name':'john', 'age':34},
... {'id':1,'name':'john', 'age':34},
... {'id':2,'name':'hanna', 'age':30},
... ] 
>>> dict((v['id'],v) for v in L).values()
[{'age': 34, 'id': 1, 'name': 'john'}, {'age': 30, 'id': 2, 'name': 'hanna'}]

【讨论】:

  • @JorgeVidinha 假设每个都可以转换为 str (或 unicode),试试这个:{str(v['flight'])+':'+str(v['lon'])+','+str(v['lat']): v for v in stream}.values() 这只是根据您的值创建一个唯一键。喜欢'MH370:-21.474370,86.325589'
  • @JorgeVidinha,您可以使用元组作为字典键{(v['flight'], v['lon'], v['lat']): v for v in stream}.values()
  • 如果您需要考虑所有值而不仅仅是 ID,您可以使用 list({str(i):i for i in L}.values()) 这里我们使用 str(i) 创建一个唯一字符串,该字符串表示用于过滤重复项的字典。
  • 这实际上并没有删除相同的字典(其中 dict1 == dict2 返回 true)。该解决方案仅在您确定要比较的关键时才有效。
  • 嘿,有人能解释一下这里到底发生了什么吗?我不知道。 list({v['id']:v for v in L}.values())
【解决方案2】:

这是我找到的解决方案:

usedID = []

x = [
{'id':1,'name':'john', 'age':34},
{'id':1,'name':'john', 'age':34},
{'id':2,'name':'hanna', 'age':30},
]

for each in x:
    if each['id'] in usedID:
        x.remove(each)
    else:
        usedID.append(each['id'])

print x

基本上你检查ID是否存在于列表中,如果存在,则删除字典,如果不存在,则将ID附加到列表中

【讨论】:

  • 我会使用一组而不是列表作为 usedID。查找速度更快,可读性更强
  • 是的,我不知道集合...但我正在学习...我只是在看@gnibbler 的答案...
  • 您需要对此进行更多测试。在迭代列表时修改列表可能并不总是按预期工作
  • 是的,我不明白为什么它不起作用...任何想法我做错了什么?
  • 不,我发现了问题......只是我不明白为什么它会出现这个问题......你知道吗?
【解决方案3】:

在集合中仅查找公共元素的常用方法是使用 Python 的 set 类。只需将所有元素添加到集合中,然后将集合转换为list,然后重复项就消失了。

当然,问题在于set() 只能包含可散列条目,而dict 不可散列。

如果我遇到这个问题,我的解决方案是将每个dict 转换为代表dict 的字符串,然后将所有字符串添加到set(),然后将字符串值读出为list()并转换回dict

dict 的字符串形式的良好表示是 JSON 格式。 Python 有一个内置的 JSON 模块(当然称为 json)。

剩下的问题是 dict 中的元素没有排序,当 Python 将 dict 转换为 JSON 字符串时,您可能会得到两个 JSON 字符串,它们表示等效的字典但不是相同的字符串。简单的解决方案是在调用json.dumps() 时传递参数sort_keys=True

编辑:这个解决方案假设给定的dict 可以有任何不同的部分。如果我们可以假设每个具有相同"id" 值的dict 将与具有相同"id" 值的所有其他dict 匹配,那么这是矫枉过正的; @gnibbler 的解决方案会更快更容易。

编辑:现在有来自 André Lima 的评论明确指出,如果 ID 是重复的,则可以安全地假设整个 dict 是重复的。所以这个答案有点矫枉过正,我推荐@gnibbler 的答案。

【讨论】:

  • 虽然在这种特殊情况下给定 ID 有点矫枉过正,但这仍然是一个很好的答案!
  • 这对我有帮助,因为我的字典没有键,并且仅由其所有条目唯一标识。谢谢!
  • 此解决方案在大多数情况下都有效,但在扩展时可能会出现性能问题,但我认为作者知道这一点,因此推荐使用“id”的解决方案。性能问题:此解决方案使用序列化为字符串,然后反序列化......序列化/反序列化是昂贵的计算,通常不能很好地扩展(项目数为 n>1e6 或每个字典包含 >1e6 项目或两者),或者如果你有执行多次 >1e6 或经常。
  • 简短地说,此解决方案说明了一个很好的典型示例,说明了您为什么要设计解决方案...即,如果您有一个唯一的 id...那么您可以有效地访问数据...如果您很懒惰并且没有 id,那么您的数据访问成本会更高。
  • 实现:` output_lod = {json.dumps(d, sort_keys=True) for d in lod} output_lod = [json.loads(x) for x in output_lod] `
【解决方案4】:

由于id 足以检测重复项,并且id 是可散列的:通过以id 作为键的字典运行它们。每个键的值都是原始字典。

deduped_dicts = dict((item["id"], item) for item in list_of_dicts).values()

在 Python 3 中,values() 不返回列表;您需要将该表达式的整个右侧包装在 list() 中,并且您可以更经济地将表达式的内容编写为 dict 理解:

deduped_dicts = list({item["id"]: item for item in list_of_dicts}.values())

请注意,结果可能与原始结果的顺序不同。如果这是一个要求,您可以使用 Collections.OrderedDict 而不是 dict

顺便说一句,将数据保留在使用id作为开始键的字典中可能很有意义。

【讨论】:

    【解决方案5】:

    这是一个相当紧凑的解决方案,虽然我怀疑不是特别有效(委婉地说):

    >>> ds = [{'id':1,'name':'john', 'age':34},
    ...       {'id':1,'name':'john', 'age':34},
    ...       {'id':2,'name':'hanna', 'age':30}
    ...       ]
    >>> map(dict, set(tuple(sorted(d.items())) for d in ds))
    [{'age': 30, 'id': 2, 'name': 'hanna'}, {'age': 34, 'id': 1, 'name': 'john'}]
    

    【讨论】:

    • 在 Python 3 中用 list() 包围 map() 调用以获取列表,否则它是 map 对象。
    • 在 python 3.6+ 中这种方法的另一个好处是保留了列表排序
    • @jnnnnn 我正在使用 Python 3.8.6 并且未保留列表排序!我的列表:x=[{'a':15}, {'a':15}, {'b':30}] 转换:list(map(dict, set(tuple(sorted(i.items())) for i in x))) 返回:[{'b': 30}, {'a': 15}]
    【解决方案6】:

    非常简单的选项:

    L = [
        {'id':1,'name':'john', 'age':34},
        {'id':1,'name':'john', 'age':34},
        {'id':2,'name':'hanna', 'age':30},
        ]
    
    
    D = dict()
    for l in L: D[l['id']] = l
    output = list(D.values())
    print output
    

    【讨论】:

      【解决方案7】:

      这是一个内存开销很小的实现,但代价是不如其他实现。

      values = [ {'id':2,'name':'hanna', 'age':30},
                 {'id':1,'name':'john', 'age':34},
                 {'id':1,'name':'john', 'age':34},
                 {'id':2,'name':'hanna', 'age':30},
                 {'id':1,'name':'john', 'age':34},]
      count = {}
      index = 0
      while index < len(values):
          if values[index]['id'] in count:
              del values[index]
          else:
              count[values[index]['id']] = 1
              index += 1
      

      输出:

      [{'age': 30, 'id': 2, 'name': 'hanna'}, {'age': 34, 'id': 1, 'name': 'john'}]
      

      【讨论】:

      • 您需要对此进行更多测试。在迭代列表时修改列表可能并不总是按预期工作
      • @gnibbler 非常好!我将删除答案并更彻底地测试它。
      • 看起来更好。您可以使用 set 来跟踪 id 而不是 dict。考虑从len(values) 开始index 并向后计数,这意味着无论您是否del,您都可以始终递减index。例如for index in reversed(range(len(values))):
      • @gnibbler 很有趣,集合是否像字典一样经常查找?
      【解决方案8】:
      a = [
      {'id':1,'name':'john', 'age':34},
      {'id':1,'name':'john', 'age':34},
      {'id':2,'name':'hanna', 'age':30},
      ]
      
      b = {x['id']:x for x in a}.values()
      
      print(b)
      

      输出:

      [{'age': 34, 'id': 1, 'name': 'john'}, {'age': 30, 'id': 2, 'name': 'hanna'}]

      【讨论】:

      • 在同一个例子中。如何获取仅包含相似 ID 的字典?
      • @user8162,您希望输出是什么样的?
      • 有时,我的ID相同,但年龄不同。所以输出为 [{'age': [34, 40], 'id': 1, 'name': ['john', Peter]}]。简而言之,如果 ID 相同,则将其他人的内容组合到我这里提到的列表中。提前致谢。
      • b = {x['id']:[y for y in a if y['id'] == x['id'] ] for x in a} 是一种分组方式他们在一起。
      【解决方案9】:

      您可以使用 numpy 库(仅适用于 Python2.x):

         import numpy as np 
      
         list_of_unique_dicts=list(np.unique(np.array(list_of_dicts)))
      

      要使其与 Python 3.x(和最新版本的 numpy)一起使用,您需要将 dicts 数组转换为 numpy 字符串数组,例如

      list_of_unique_dicts=list(np.unique(np.array(list_of_dicts).astype(str)))
      

      【讨论】:

      【解决方案10】:

      如果字典仅由所有项目唯一标识(ID 不可用),您可以使用 JSON 来使用答案。以下是不使用 JSON 的替代方案,只要所有字典值都是不可变的,就可以使用

      [dict(s) for s in set(frozenset(d.items()) for d in L)]
      

      【讨论】:

        【解决方案11】:

        一个快速而简单的解决方案就是生成一个新列表。

        sortedlist = []
        
        for item in listwhichneedssorting:
            if item not in sortedlist:
                sortedlist.append(item)
        

        【讨论】:

          【解决方案12】:

          扩展 John La Rooy (Python - List of unique dictionaries) 的答案,使其更加灵活:

          def dedup_dict_list(list_of_dicts: list, columns: list) -> list:
              return list({''.join(row[column] for column in columns): row
                          for row in list_of_dicts}.values())
          

          调用函数:

          sorted_list_of_dicts = dedup_dict_list(
              unsorted_list_of_dicts, ['id', 'name'])
          

          【讨论】:

            【解决方案13】:

            我不知道您是否只希望列表中的 dicts 的 id 是唯一的,但如果目标是拥有一组 dict ,其中唯一性是所有键的值..您应该使用元组在你的理解中这样的关键:

            >>> L=[
            ...     {'id':1,'name':'john', 'age':34},
            ...    {'id':1,'name':'john', 'age':34}, 
            ...    {'id':2,'name':'hanna', 'age':30},
            ...    {'id':2,'name':'hanna', 'age':50}
            ...    ]
            >>> len(L)
            4
            >>> L=list({(v['id'], v['age'], v['name']):v for v in L}.values())
            >>>L
            [{'id': 1, 'name': 'john', 'age': 34}, {'id': 2, 'name': 'hanna', 'age': 30}, {'id': 2, 'name': 'hanna', 'age': 50}]
            >>>len(L)
            3
            

            希望它可以帮助您或其他有问题的人......

            【讨论】:

              【解决方案14】:

              在 python 3.6+(我已经测试过)中,只需使用:

              import json
              
              #Toy example, but will also work for your case 
              myListOfDicts = [{'a':1,'b':2},{'a':1,'b':2},{'a':1,'b':3}]
              #Start by sorting each dictionary by keys
              myListOfDictsSorted = [sorted(d.items()) for d in myListOfDicts]
              
              #Using json methods with set() to get unique dict
              myListOfUniqueDicts = list(map(json.loads,set(map(json.dumps, myListOfDictsSorted))))
              
              print(myListOfUniqueDicts)
              

              说明:我们正在映射json.dumps 以将字典编码为不可变的 json 对象。 set 然后可用于生成 unique 不可变的迭代。最后,我们使用json.loads 转换回我们的字典表示。请注意,最初,必须按键排序才能以独特的形式排列字典。这对 Python 3.6+ 有效,因为字典是默认排序的。

              【讨论】:

              • 记得在转储到 JSON 之前对键进行排序。在执行set 之前,您也不需要转换为list
              【解决方案15】:

              这里有很多答案,我再补充一个:

              import json
              from typing import List
              
              def dedup_dicts(items: List[dict]):
                  dedupped = [ json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in items)]
                  return dedupped
              
              items = [
                  {'id': 1, 'name': 'john', 'age': 34},
                  {'id': 1, 'name': 'john', 'age': 34},
                  {'id': 2, 'name': 'hanna', 'age': 30},
              ]
              dedup_dicts(items)
              

              【讨论】:

                【解决方案16】:

                我们可以使用pandas

                import pandas as pd
                yourdict=pd.DataFrame(L).drop_duplicates().to_dict('r')
                Out[293]: [{'age': 34, 'id': 1, 'name': 'john'}, {'age': 30, 'id': 2, 'name': 'hanna'}]
                

                注意与接受答案略有不同。

                drop_duplicates 将检查 pandas 中的所有列,如果都相同,则该行将被删除。

                例如:

                如果我们将第二个 dict 名称从 john 更改为 peter

                L=[
                    {'id': 1, 'name': 'john', 'age': 34},
                    {'id': 1, 'name': 'peter', 'age': 34},
                    {'id': 2, 'name': 'hanna', 'age': 30},
                ]
                pd.DataFrame(L).drop_duplicates().to_dict('r')
                Out[295]: 
                [{'age': 34, 'id': 1, 'name': 'john'},
                 {'age': 34, 'id': 1, 'name': 'peter'},# here will still keeping the dict in the out put 
                 {'age': 30, 'id': 2, 'name': 'hanna'}]
                

                【讨论】:

                • 这是一个好技巧,但需要注意的是,这不适用于嵌套字典。
                【解决方案17】:

                我总结了我最喜欢的尝试:

                https://repl.it/@SmaMa/Python-List-of-unique-dictionaries

                # ----------------------------------------------
                # Setup
                # ----------------------------------------------
                
                myList = [
                  {"id":"1", "lala": "value_1"},
                  {"id": "2", "lala": "value_2"}, 
                  {"id": "2", "lala": "value_2"}, 
                  {"id": "3", "lala": "value_3"}
                ]
                print("myList:", myList)
                
                # -----------------------------------------------
                # Option 1 if objects has an unique identifier
                # -----------------------------------------------
                
                myUniqueList = list({myObject['id']:myObject for myObject in myList}.values())
                print("myUniqueList:", myUniqueList)
                
                # -----------------------------------------------
                # Option 2 if uniquely identified by whole object
                # -----------------------------------------------
                
                myUniqueSet = [dict(s) for s in set(frozenset(myObject.items()) for myObject in myList)]
                print("myUniqueSet:", myUniqueSet)
                
                # -----------------------------------------------
                # Option 3 for hashable objects (not dicts)
                # -----------------------------------------------
                
                myHashableObjects = list(set(["1", "2", "2", "3"]))
                print("myHashAbleList:", myHashableObjects)
                

                【讨论】:

                  【解决方案18】:

                  这里提到的所有答案都很好,但是在某些答案中,如果字典项具有嵌套列表或字典,可能会遇到错误,所以我建议简单的答案

                  a = [str(i) for i in a]
                  a = list(set(a))
                  a = [eval(i) for i in a]
                  

                  【讨论】:

                    【解决方案19】:

                    在 python 3 中,简单的技巧,但基于唯一字段 (id):

                    data = [ {'id': 1}, {'id': 1}]
                    
                    list({ item['id'] : item for item in data}.values())
                    

                    【讨论】:

                      【解决方案20】:

                      对象可以放入集合中。您可以使用对象而不是字典,如果需要,在所有集合插入转换回字典列表之后。示例

                      class Person:
                          def __init__(self, id, age, name):
                              self.id = id
                              self.age = age
                              self.name = name
                      
                      my_set = {Person(id=2, age=3, name='Jhon')}
                      
                      my_set.add(Person(id=3, age=34, name='Guy'))
                      
                      my_set.add({Person(id=2, age=3, name='Jhon')})
                      
                      # if needed convert to list of dicts
                      list_of_dict = [{'id': obj.id,
                                       'name': obj.name,
                                       'age': obj.age} for obj in my_set]
                      

                      【讨论】:

                      • 一种更简洁的 Person 定义方式:Person = collections.namedtuple('Person', ['id', 'age', 'name'])
                      【解决方案21】:

                      让我添加我的。

                      1. 对目标字典进行排序,以便 {'a' : 1, 'b': 2} 和​​ {'b': 2, 'a': 1} 不会被区别对待

                      2. 把它做成json

                      3. 通过 set 去重(因为 set 不适用于 dicts)

                      4. 再次,通过 json.loads 将其转换为 dict

                      import json
                      
                      [json.loads(i) for i in set([json.dumps(i) for i in [dict(sorted(i.items())) for i in target_dict]])]
                      

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 2017-11-02
                        • 2013-06-14
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2016-11-16
                        相关资源
                        最近更新 更多