【问题标题】:Python: Combining unique values in list of dicts where keys are the same?Python:在键相同的字典列表中组合唯一值?
【发布时间】:2021-04-17 12:36:27
【问题描述】:

我不确定我是否以正确的方式提出问题,但这是我的问题:

我有以下格式的字典列表:

[
{'user': 'joe', 'IndexUsed': 'a'}, 
{'user': 'joe', 'IndexUsed': 'a'},
{'user': 'joe', 'IndexUsed': 'a'},
{'user': 'joe', 'IndexUsed': 'b'}, 
{'user': 'admin', 'IndexUsed': 'a'}, 
{'user': 'admin', 'IndexUsed': 'c'},
{'user': 'hugo', 'IndexUsed': 'a'},
{'user': 'hugo', 'IndexUsed': 'd'},
...
]

我希望我的最终结果如下所示:

[
{'user': 'joe', 'IndexUsed': ['a', 'b']}, 
{'user': 'admin', 'IndexUsed': ['a', 'c']}, 
{'user': 'hugo', 'IndexUsed': ['a', 'd']},
]

本质上,合并/去重IndexUsed 中的唯一字段并将它们减少到每个user 中只有一个字典

我已经研究过使用 reducer、dict 理解,并在 StackOverflow 上进行了搜索,但我在使用字符串查找用例时遇到了一些麻烦。我发现的大多数示例都是使用整数将它们组合成最终的 int/float,但在这里我宁愿将它组合成一个最终的字符串。你能帮我理解如何解决这个问题吗?

【问题讨论】:

  • 所以您希望输出保留为字典列表,但 IndexUsed 键被合并到列表中?

标签: python python-3.x list dictionary-comprehension


【解决方案1】:

如果保证字典按名称分组在一起,那么您可以使用itertools.groupby 分别处理每组字典:

from itertools import groupby
from operator import itemgetter

data = [
    {'user': 'joe', 'IndexUsed': 'a'},
    {'user': 'joe', 'IndexUsed': 'a'},
    {'user': 'joe', 'IndexUsed': 'a'},
    {'user': 'joe', 'IndexUsed': 'b'},
    {'user': 'admin', 'IndexUsed': 'a'},
    {'user': 'admin', 'IndexUsed': 'c'},
    {'user': 'hugo', 'IndexUsed': 'a'},
    {'user': 'hugo', 'IndexUsed': 'd'},
]

merged_data = [{"user": key, "IndexUsed": list({i: None for i in map(itemgetter("IndexUsed"), group)})} for key, group in groupby(data, key=itemgetter("user"))]
for d in merged_data:
    print(d)

输出:

{'user': 'joe', 'IndexUsed': ['a', 'b']}
{'user': 'admin', 'IndexUsed': ['a', 'c']}
{'user': 'hugo', 'IndexUsed': ['a', 'd']}
>>> 

这只是我想出的第一件事,但我不喜欢它有几个原因。首先,就像我说的,它假设原始字典由键 user 组合在一起。此外,长列表理解是不可读的,应该避免。生成合并的IndexUsed 列表的方式是创建一个临时字典,该字典将唯一条目映射到None (ew,总 - 使用字典而不是集合,因为集合不保留插入顺序)。它还假设您使用的是特定版本的 Python 3.x+,其中字典保证保留插入顺序(您可以使用 collections.OrderedDict 更明确,但这是一个更重要的地方)。最后,您不必对"user""IndexUsed" 键字面值进行硬编码。有人请提出更好的答案。

【讨论】:

    【解决方案2】:
    from collections import defaultdict
    
    
    data = [{'IndexUsed': 'a', 'user': 'joe'},
     {'IndexUsed': 'a', 'user': 'joe'},
     {'IndexUsed': 'a', 'user': 'joe'},
     {'IndexUsed': 'b', 'user': 'joe'},
     {'IndexUsed': 'a', 'user': 'admin'},
     {'IndexUsed': 'c', 'user': 'admin'},
     {'IndexUsed': 'a', 'user': 'hugo'},
     {'IndexUsed': 'd', 'user': 'hugo'}]
    
    indexes_used = defaultdict(set)
    for d in data:
        indexes_used[d['user']].add(d['IndexUsed'])
    
    result = []
    for k, v in indexes_used.items():
        result.append({'user': k, 'IndexUsed': sorted(list(v))})
    
    print(*result)
    

    输出:

    {'user': 'joe', 'IndexUsed': ['a', 'b']} {'user': 'admin', 'IndexUsed': ['a', 'c']} {'user': 'hugo', 'IndexUsed': ['a', 'd']}
    

    注意:对于不知情的人,defaultdict 使用传递的函数(在本例中为set)作为工厂来创建新的缺失键对应值。所以indexes_used 的每一个键都被设置 为一个set 填充使用的索引。使用 set 也会忽略重复项。最后将set 转换为排序列表,同时创建所需的键IndexUsed

    【讨论】:

      【解决方案3】:

      如果您有兴趣,可以在不使用任何库的情况下解决此要求:

      arr = [
      {'user': 'joe', 'IndexUsed': 'a'}, 
      {'user': 'joe', 'IndexUsed': 'a'},
      {'user': 'joe', 'IndexUsed': 'a'},
      {'user': 'joe', 'IndexUsed': 'b'}, 
      {'user': 'admin', 'IndexUsed': 'a'}, 
      {'user': 'admin', 'IndexUsed': 'c'},
      {'user': 'hugo', 'IndexUsed': 'a'},
      {'user': 'hugo', 'IndexUsed': 'd'},
      ]
      
      global_dict = {}
      
      
                  
      for d in arr:
      
      
           if(False if d["user"] in global_dict else True):
      
                  global_dict[d["user"]] = [d["IndexUsed"]]
           else:
                  global_dict[d["user"]].append(d["IndexUsed"])
                  global_dict[d["user"]] = list(set(global_dict[d["user"]]))
       
      
      print(global_dict)
      
      # Now we get a dict of dicts with key as user and value as an array of distinct IndexUsed values: 
      # {
      #  'joe': ['b', 'a'],
      #  'admin': ['c', 'a'],
      #  'hugo': ['d', 'a']
      # }
      
      
      
      final_list = []
      
      for k,v in global_dict.items():
          final_list.append({"user":k,"IndexUsed":v})
      
      
      print(final_list)
      
      #Desired Output
      # [
      #  {'user': 'joe', 'IndexUsed': ['b', 'a']},
      #  {'user': 'admin', 'IndexUsed': ['c', 'a']},
      #  {'user': 'hugo', 'IndexUsed': ['d', 'a']}
      # ]
      

      但是,如果您是短线爱好者... 让我将@progmatico 很棒的 defaultdict 方法最小化为这三行。

      from collections import defaultdict
      
      
      indexes_used = defaultdict(set)
      [indexes_used[d['user']].add(d['IndexUsed']) for d in data] # for the side effect
      print([{'user': k, 'IndexUsed': sorted(list(v))} for k, v in indexes_used.items()])
      

      而且它仍然可读。

      【讨论】:

      • 谢谢,刚刚编辑了一点。我认为工作和可读就足够了。在 Python 中,你永远不应该假设你是用最 Pythonic 的方式制作的。可能还有另一种惯用的方式。虽然优雅通常会表现出来,但我看到太多人担心自己是 pythonic
      • @progmatico,我完全同意你的看法。
      【解决方案4】:

      没有任何外部库:

      l = [
          {'user': 'joe', 'IndexUsed': 'a'}, 
          {'user': 'joe', 'IndexUsed': 'a'},
          {'user': 'joe', 'IndexUsed': 'a'},
          {'user': 'joe', 'IndexUsed': 'b'}, 
          {'user': 'admin', 'IndexUsed': 'a'}, 
          {'user': 'admin', 'IndexUsed': 'c'},
          {'user': 'hugo', 'IndexUsed': 'a'},
          {'user': 'hugo', 'IndexUsed': 'd'}
      ]
      
      def combinator(l):
          d = {}
              
          for item in l:
              if(d.get(item['user']) == None):
                  d[item['user']] = {item['IndexUsed']}
                  pass
              d[item['user']].add(item['IndexUsed'])
              
          return [{'user': key, 'IndexUsed': sorted(value)} for key, value in d.items()]
      
      
      print(combinator(l))
      

      【讨论】:

        猜你喜欢
        • 2018-01-20
        • 1970-01-01
        • 2015-08-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-26
        • 1970-01-01
        • 2017-04-20
        相关资源
        最近更新 更多