【问题标题】:Append to a nested list in a list of dicts under conditions在条件下附加到字典列表中的嵌套列表
【发布时间】:2021-07-02 12:54:11
【问题描述】:

我有 2 个共享信息的列表。首先,我想要一组唯一的名称(例如list_person 重复了name 值);为此,我制作了一个新的字典列表。 然后,当list_pets['person_id']list_person['id'] 匹配时,我想将list_pets['pet'] 添加/附加到具有唯一名称值的新字典中正确的list_person['pets']

为了澄清,这里是我的代码+所需的输出:

我当前的代码:

list_person = [{'id': 12345, 'name': 'Bobby Bobs', 'pets': ['cat']}, # you see that name values are repeated
              {'id': 678910, 'name': 'Bobby Bobs', 'pets': ['zebra']},
              {'id': 111213, 'name': 'Lisa Bobs', 'pets': ['horse']},
              {'id': 141516, 'name': 'Lisa Bobs', 'pets': ['rabbit']}]

list_pets = [{'id': 'abcd', 'pet': 'shark', 'person_id': 12345}, #Bobby Bobs' pets
             {'id': 'efgh', 'pet': 'tiger', 'person_id': 678910}, #Bobby Bobs' pets
             {'id': 'ijkl', 'pet': 'elephant', 'person_id': 111213}, #Lisa Bobs' pets
             {'id': 'mnopq', 'pet': 'dog', 'person_id': 141516}] #Lisa Bobs' pets

output = []
for person, pet in zip(list_person, list_pets):
    t = [temp_dict['name'] for temp_dict in output]
    if person['name'] not in t:
        output.append(person)    # make a new list of dicts with unique name values
        for unique_person in output: # if they share ID, add the missing pets. 
            if person['id'] == pet['person_id']:
                unique_person['pets'].append(pet['pet'])
print(output)

期望的输出:

desired_out = [{'id': 12345, 'name': 'Bobby Bobs', 'pets': ['cat', 'zebra', 'shark', 'tiger']},
                {'id': 111213, 'name': 'Lisa Bobs', 'pets': ['horse', 'rabbit', 'elephant', 'dog']}]

当前输出:

[{'id': 12345, 'name': 'Bobby Bobs', 'pets': ['cat', 'shark', 'elephant']}, {'id': 111213, 'name': 'Lisa Bobs', 'pets': ['horse', 'elephant']}]

我当前的输出没有显示所有正确的宠物。这是为什么;为了更接近解决方案,有人会给我什么建议?

【问题讨论】:

  • 你当前的代码输出什么?
  • 我用我当前的输出对问题进行了编辑。上面的所有代码都是可重现的。 :) 谢谢
  • 输出的 id 应该是名称的第一次出现
  • 这似乎是XY Problem。您在应该使用数据框的地方使用了dicts;您的 ID 不是唯一的。如果你“正确地”设计这个,这是一个简单的数据框merge 和一个groupby。是否有一些系统要求会导致设计决策看起来很糟糕?
  • @Rajesh C 不一定是这样。重要的是在字典列表中拥有该名称一次,以及与该人相关的所有宠物。换句话说,没有信息丢失。

标签: python list dictionary nested


【解决方案1】:

这是一个非 Pandas 解决方案,它不依赖于 list_person(又名“人”)和 list_pets 之间的顺序关系。所以我不假设 Bobby 的数据是两个列表中的前两个条目。

最初,output 将是姓名到个人数据(包括宠物)的映射。并且ids 将被维护以链接每个人的不同 ID - 通过有意使用对数据字典的引用而不是副本。

请注意,当一个人被添加到output 时,它是作为 deepcopy 完成的,因此它不会影响 list_person 中的原始项目。

import copy

output = {}  # dict, not list
ids = {}  # needed to match with pets which has person_id

for person in list_person:
    if (name := person['name']) in output:
        output[name]['pets'].extend(person['pets'])
        output[name]['id'].append(person['id'])
        ids[person['id']] = output[name]  # itentionally a reference, not a copy
    else:
        output[name] = copy.deepcopy(person)  # so that the pet list is created as a copy
        output[name]['id'] = [output[person['name']]['id']]  # turn id's into a list
        ids[person['id']] = output[name]  # itentionally a reference, not a copy

for pet in list_pets:
    # the values in ids dict can be references to the same object
    # so use that to our advantage by directly appending to 'pet' list
    ids[pet['person_id']]['pets'].append(pet['pet'])

output 现在是:

{'Bobby Bobs': {'id': [12345, 678910],
                'name': 'Bobby Bobs',
                'pets': ['cat', 'zebra', 'shark', 'tiger']},
 'Lisa Bobs': {'id': [111213, 141516],
               'name': 'Lisa Bobs',
               'pets': ['horse', 'rabbit', 'elephant', 'dog']}
}

最后一步,使其成为一个列表,每个人只使用一个id

output = list(output.values())
for entry in output:
    entry['id'] = entry['id'][0]  # just the first id

最终output:

[{'id': 12345,
  'name': 'Bobby Bobs',
  'pets': ['cat', 'zebra', 'shark', 'tiger']},
 {'id': 111213,
  'name': 'Lisa Bobs',
  'pets': ['horse', 'rabbit', 'elephant', 'dog']}]

如果您不介意多个 ID,请跳过上面的最后一步并将其保留在 output = list(output.values())

【讨论】:

  • 解释得很好,可读性也很好。学到了很多,谢谢!
【解决方案2】:
import itertools
person_df = pd.DataFrame(list_person)
pets_df = pd.DataFrame(list_pets).drop(columns = ['id'])
joined_df = person_df.merge(pets_df, left_on = ['id'], right_on = ['person_id'])

加入df:

>>> joined_df
       id        name               pets       pet  person_id
0   12345  Bobby Bobs       [cat, shark]     shark      12345
1  678910  Bobby Bobs     [zebra, tiger]     tiger     678910
2  111213   Lisa Bobs  [horse, elephant]  elephant     111213
3  141516   Lisa Bobs      [rabbit, dog]       dog     141516

现在先组合宠物和宠物列,然后按名称分组

joined_df['pets'] = [pets + [pet] for pets, pet in zip(joined_df['pets'], joined_df['pet'])]
final_list = joined_df.groupby('name', as_index = False).agg(
                                  id = ('id', 'first'), 
                                  pets = ('pets', lambda x: list(itertools.chain(*x)))
                                ).to_dict('records')

输出:

>>> final_list
 [{'name': 'Bobby Bobs', 'id': 12345, 'pets': ['cat', 'shark', 'zebra', 'tiger']}, 
{'name': 'Lisa Bobs', 'id': 111213, 'pets': ['horse', 'elephant', 'rabbit', 'dog']}]

【讨论】:

    猜你喜欢
    • 2020-06-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-08
    • 1970-01-01
    • 2023-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多