【问题标题】:Python: Combine all dict key values based on one particular key being the samePython:基于一个特定键相同的所有dict键值组合
【发布时间】:2020-06-04 15:35:11
【问题描述】:

我知道有一百万个这样的问题,我只是找不到适合我的答案。

我有这个:

list1 =   [{'assembly_id': '1', 'asym_id_list': ['A', 'B', 'E', 'G', 'H']}, {'assembly_id': '1', 'asym_id_list': ['C', 'D', 'F', 'I', 'J']}, {'assembly_id':2,'asym_id_list':['D,C'],'auth_id_list':['C','V']}]

如果 assembly_ids 相同,我想在 dict 中组合其他相同的键。

在这个例子中,assembly_id 1 出现了两次,所以上面的输入会变成:

[{'assembly_id': '1', 'asym_id_list': ['A', 'B', 'E', 'G', 'H','C', 'D', 'F', 'I', 'J']},{'assembly_id':2,'asym_id_list:['D,C'],'auth_id_list':['C','V']}]

理论上可以有 n 个 assembly_id(即,程序集 1 可能出现在 dict 中 10 或 20 次,而不仅仅是 2 次),并且最多可以有两个其他列表(asym_id_list 和 auth_id_list)。

我在看这个方法:

new_dict = {}
assembly_list = [] #to keep track of assemblies already seen
for dict_name in list1: #for each dict in the list
        if dict_name['assembly_id'] not in assembly_list: #if the assembly id is new
                new_dict['assembly_id'] = dict_name #this line is wrong, add the entry to new_dict
                assembly_list.append(new_dict['assembly_id']) #append the id to 'assembly_list'
        else:
                new_dict['assembly_id'].append(dict_name) #else if it's already seen, append the dictionaries together, this is wrong
print(new_dict)

输出错误:

{'assembly_id': {'assembly_id': 2, 'asym_id_list': ['D,C'], 'auth_id_list': ['C', 'V']}}

但我认为这个想法是对的,我应该打开一个新列表和字典,如果以前没有见过,追加;而如果它以前见过......结合?但这只是我没有得到的细节?

【问题讨论】:

  • 重新考虑最外层的循环:您想要返回一个字典列表。然后,您可以将 dict_name 附加到该列表中,如果之前没有看到过 dict_name['assembly_list'],您可以添加列表 'asym_id_list''auth_id_list' 如果之前看到过。
  • 我认为您的示例数据中有几个拼写错误。我认为您的意思是'assembly_id':2,'asym_id_list':['D,C'] 中的'D,C' 是这样的单独字符串:'assembly_id':2,'asym_id_list':['D', 'C']。同样在“assembly_id”键中,您可以混合使用字符串和整数(即'1'2)。尽管这会起作用,但我猜您并不打算将密钥混合为 ints 和 strings

标签: python dictionary merge


【解决方案1】:

您的逻辑思维正确,我们可以使用字典m,其中包含assembly_id 的键、值对及其对应的字典来跟踪访问过的assembly_ids,每当遇到新的assembly_id 时,我们添加它到字典m,否则如果它已经包含assembly_id,我们只需扩展asym_id_listauth_id_listassembly_id

def merge(dicts):
    m = {} # keeps track of the visited assembly_ids
    for d in dicts:
        key = d['assembly_id'] # assembly_id is used as merge/grouping key
        if key in m:
            if 'asym_id_list' in d:
                m[key]['asym_id_list'] = m[key].get('asym_id_list', []) + d['asym_id_list']
            elif 'auth_id_list' in d:
                m[key]['auth_id_list'] = m[key].get('auth_id_list', []) + d['auth_id_list']
        else:
            m[key] = d
    return list(m.values())

结果:

# merge(list1)
[
    {
        'assembly_id': '1', 'asym_id_list': ['A', 'B', 'E', 'G', 'H', 'C', 'D', 'F', 'I', 'J']
    },
    {
        'assembly_id': 2, 'asym_id_list': ['D,C'], 'auth_id_list': ['C', 'V']
    }
]

【讨论】:

  • 这段代码有一个小错误。elif 'auth_id_list' in d:不应该是elif,该行应该是if 'auth_id_list' in d:,因为条目可以同时包含'asym_id_list'和@987654336 @ 条目。
【解决方案2】:

使用键入assembly_id 的字典来收集给定键的所有数据;如果需要,您可以返回并生成原始格式的字典列表。

>>> from collections import defaultdict
>>> from typing import Dict, List
>>> id_lists: Dict[str, List[str]] = defaultdict(list)
>>> for d in list1:
...     id_lists[d['assembly_id']].extend(d['asym_id_list'])
...
>>> combined_list = [{
...     'assembly_id': id, 'asym_id_list': id_list
... } for id, id_list in id_lists.items()]
>>> combined_list
[{'assembly_id': '1', 'asym_id_list': ['A', 'B', 'E', 'G', 'H', 'C', 'D', 'F', 'I', 'J']}, {'assembly_id': 2, 'asym_id_list': ['D,C']}]
>>>

(edit) 没有看到关于 auth_id_lists 的内容,因为它隐藏在原始代码的滚动条中——同样的策略适用,只需在第一步中使用两个 dicts 或使其成为一些集合的 dict列表(例如,列表字典的字典,外部字典键入 assembly_id 值,内部字典键入原始字段名称)。

【讨论】:

    【解决方案3】:

    @Samwise 已为您提出的问题提供了一个很好的答案,这并不打算取代它。但是,我将对您在合并后保留数据的方式提出建议。我会把它放在评论中,但没有办法在评论中保留代码格式,而且它也有点太大了。

    在此之前,我认为您的示例数据中有错字。我认为您的意思是'assembly_id':2,'asym_id_list':['D,C'] 中的'D,C' 是这样的单独字符串:'assembly_id':2,'asym_id_list':['D', 'C']。我将在下面假设,但如果不是,它不会更改任何代码或 cmets。

    而不是像这样的字典列表:

    merge_l = [
                {'assembly_id': '1', 'asym_id_list': ['A', 'B', 'E', 'G', 'H', 'C', 'D', 'F', 'I', 'J']},
                {'assembly_id': 2, 'asym_id_list': ['D', 'C'], 'auth_id_list': ['C', 'V']}
              ]
    

    相反,我建议不要使用列表作为顶级结构,而是使用由 assembly_id 的值作为键的字典。所以这将是一个字典,其值是字典。像这样:

    merge_d = { '1': {'asym_id_list': ['A', 'B', 'E', 'G', 'H', 'C', 'D', 'F', 'I', 'J']},
                '2': {'asym_id_list': ['D', 'C'], 'auth_id_list': ['C', 'V']}
              }
    

    或者如果您也想保留“assembly_id”,如下所示:

    merge_d = { '1': {'assembly_id': '1', 'asym_id_list': ['A', 'B', 'E', 'G', 'H', 'C', 'D', 'F', 'I', 'J']},
                '2': {'assembly_id': 2, 'asym_id_list': ['D', 'C'], 'auth_id_list': ['C', 'V']}
              }
    

    最后一个可以通过更改@Samwise 的merge() 方法的返回值和return m 而不是将dict 转换为列表来实现。

    关于@Samwise 代码的另一条评论,只是为了让您知道,组合列表可以包含重复项。因此,如果原始数据在一个条目中有asym_id_list': ['A', 'B'],在另一个条目中有asym_id_list': ['B', 'C'],则组合列表将包含asym_id_list': ['A', 'B', 'B', 'C']。这可能是您想要的,但如果您想避免这种情况,您可以为 asym_id 和 auth_id 容器的内部容器使用集合而不是列表。

    在@Samwise 答案中,将其更改为:

    def merge(dicts):
        m = {} # keeps track of the visited assembly_ids
        for d in dicts:
            key = d['assembly_id'] # assembly_id is used as merge/grouping key
            if key in m:
                if 'asym_id_list' in d:
                    m[key]['asym_id_list'] = m[key].get('asym_id_list', set()) | set(d['asym_id_list'])
                if 'auth_id_list' in d:
                    m[key]['auth_id_list'] = m[key].get('auth_id_list', set()) | set(d['auth_id_list'])
            else:
                m[key] = {'assembly_id': d['assembly_id']}
                if 'asym_id_list' in d:
                    m[key]['asym_id_list'] = set(d['asym_id_list'])
                if 'auth_id_list' in d:
                    m[key]['auth_id_list'] = set(d['auth_id_list'])
        return m
    

    如果您这样做,您可能需要重新考虑键名'asym_id_list''auth_id_list',因为它们是集合而不是列表。但这可能会受到其他代码及其预期的限制。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多