【问题标题】:Creating a list of dictionaries with numerous key value pairs创建具有大量键值对的字典列表
【发布时间】:2022-01-24 21:25:36
【问题描述】:

我有多个列表,我需要将它们组合成具有多个键值对的字典列表,其中值本身可以包含一个列表 - 例如:

namesIDs = [1, 2, 3, 4, 5]
namesList = ['jacksparrow', 'aragron', 'harrypotter', 'bilbo', 'einstein']
address = ['addr1', 'addr2', 'addr3', 'addr4', 'addr5']
parentsIDs = [11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34, 41, 42, 43, 44, 51, 52, 53, 54, 55, 56] 
parentsNames = ['Teague', 'MrsT', 'gPaT', 'gMaT', 'Arathorn', 'Gilraen', 'gPaAg', 'gMaAg', 'James', 'Lily', 'gPaHp', 'gMaHp', 'Bungo', 'Belladona', 'gPaB', 'gMaB', 'Herman', 'Pauline', 'Abraham', 'Helene', 'babyA', 'babyB']

我期待的输出是:

[{'nameIDs': 1, 'namesList': 'jacksparrow', 'address': 'addr1', 'parentsIDs': [11,12,13,14], 'parentsNames': ['Teague', 'MrsT', 'gPaT', 'gMaT']},
 {'nameIDs': 2, 'namesList': 'aragorn', 'address': 'addr2', 'parentsIDs': [21,22,23,24], 'parentsNames': ['Arathorn', 'Gilraen', 'gPaAg', 'gMaAg']},
 {'nameIDs': 3, 'namesList': 'harrypotter', 'address': 'addr3', 'parentsIDs': [31,32,33,34], 'parentsNames': ['James', 'Lily', 'gPaHp', 'gMaHp']},
 {'nameIDs': 4, 'namesList': 'bilbo', 'address': 'addr4', 'parentsIDs': [41,42,43,44], 'parentsNames': ['Bungo', 'Belladona', 'gPaB', 'gMaB']},
 {'nameIDs': 5, 'namesList': 'einstein', 'address': 'addr5', 'parentsIDs': [51,52,53,54,55,56], 'parentsNames': ['Bungo', 'Belladona', 'gPaB', 'gMaB', 'babyA', 'babyB']}
]

注意最后一项 parentsIDs/parentsNames 比其余的要长。 我尝试过使用 zip 和 dict 理解,但它对我不起作用/没有意义。

>>> namesIDs = [1, 2, 3, 4, 5]
>>> namesList = ['jacksparrow', 'aragron', 'harrypotter', 'bilbo', 'einstein']
>>> [dict(zip(('NameID', 'Name'), item )) for item in namesList]
[{'NameID': 'j', 'Name': 'a'}, {'NameID': 'a', 'Name': 'r'}, {'NameID': 'h', 'Name': 'a'}, {'NameID': 'b', 'Name': 'i'}, {'NameID': 'e', 'Name': 'i'}]

编辑: 请注意,parentsIDs 和 parentsNames 可能是任意长的。 parentsID 的第一个数字决定了 parentsID 对应的 nameID。

【问题讨论】:

  • zip 不起作用,因为您的最后 2 个列表长度不等于其他 3 个
  • 除非您知道这些列表是如何创建的,否则很难将它们放回字典形式。例如,在这个 [{'nameIDs': 1, 'namesList': 'jacksparrow', 'address': 'addr1', 'parentsIDs': [11,12,13,14], 'parentsNames': ['Teague', 'MrsT', 'gPaT', 'gMaT']}, 中,您是如何确定 parentsIDs': [11,12,13,14] 只是这 4 个值而不是 5 个?
  • @Anu 因为他的总列表长度是 20,他想把它分成 5 个组
  • 嗯,有道理。我看到了您的解决方案,看起来应该可以解决问题
  • 请注意,问题中遗漏了一条关键信息(仅在答案的 cmets 中进行了澄清)。由于parentsID(和parentNames)的数量是任意长的,因此问题中没有明确的方法可以知道哪个parentsID与哪个namesID一起使用(并且它没有像@Dahal评论的那样均匀分配)。后来的 cmets 澄清了 parentsID 的 first 数字表示它与哪个 namesID 相关联(即 11-14 与 1 相关联,21-24 与 2 相关联,等等)。这应该在问题中说明。

标签: python list dictionary list-comprehension dictionary-comprehension


【解决方案1】:

您可以为最后 2 个列表创建列表列表,然后使用 ziplist_comprehension

from collections import defaultdict

namesIDs = [1, 2, 3, 4, 5]
namesList = ['jacksparrow', 'aragron', 'harrypotter', 'bilbo', 'einstein']
address = ['addr1', 'addr2', 'addr3', 'addr4', 'addr5']
parentsNames = ['Teague', 'MrsT', 'gPaT', 'gMaT', 'Arathorn', 'Gilraen', 'gPaAg', 'gMaAg', 'James', 'Lily', 'gPaHp', 'gMaHp', 'Bungo', 'Belladona', 'gPaB', 'gMaB', 'Herman', 'Pauline', 'Abraham', 'Helene']
parentsIDs = [11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34, 41, 42, 43, 44, 51, 52, 53, 54] 


kv = {k:list(range(k*10, (k*10+10))) for k in namesIDs}
nd = defaultdict(list)
npnts = defaultdict(list)
for ele,pn in zip(parentsIDs, parentsNames):
  for k, v in kv.items():
    if ele in v:
      nd[k].append(ele)
      npnts[k].append(pn)
nd = dict(nd)
npnts = dict(npnts)

parentsIDs = [v for k,v in nd.items()]
parentsNames = [v for k,v in npnts.items()]

final_dict = [{"nameIDs":n,"namesList":nl, "address":ad, 'parentsIDs':pid, 'parentsNames':pn} for n,nl,ad,pid,pn in zip(namesIDs,namesList,address,parentsIDs,parentsNames)]

print(final_dict)

【讨论】:

  • Dahl,您建议的解决方案将 parentID 和父母名称映射为 [[11, 12, 13, 14], [22, 23, 24, 31], [33, 34, 41 , 42], [44, 51, 52, 53]] 而不是 [[11, 12, 13, 14], [21, 22, 23, 24], [31, 32, 33, 34], [41, 42、43、44]]。此外,我需要 parentsNames/parentsIDs 列表中任意数量的元素,这样如果数字不匹配,zip 可能无法工作。您能否检查一下您的答案
  • 您的解决方案仅在 parentIDs/parentsNames 的元素长度全部匹配时才有效,但在 parentIDs/parentsName 之一具有其他元素时无效 - 我的要求是满足 parentsIDs 中任意数量的元素/parentsNames 列表。
  • @KazSalvatore 你能通过添加你想要的内容和举例来更新你的主要问题吗?
  • @KazSalvatore 所以你想要所有列表的 4 个项目。除了可以包含多于或少于 4 个的最后一个列表?
  • 不,parentsIDs/parentsNames 可以有任意长度,但通过查看元素 (51),您知道它对应于 dict 中的第 5 个元素,随后与 51 相同索引的 parentsNames 也对应于dict中的第5个元素。
【解决方案2】:

您可以在以下列表推导中使用列表切片。

parentsIDsLists = [[]]
parentsNamesLists = [[]]
for i,j in zip(map(str, parentsIDs), parentsNames):
    if parentsIDsLists[-1] != [] and i[0] != parentsIDsLists[-1][-1][0]:
        parentsIDsLists.append([])
        parentsNamesLists.append([])
    parentsIDsLists[-1].append(i)
    parentsNamesLists[-1].append(j)

out = [dict(zip(('nameIDs', 'namesList', 'address', 'parentsIDs', 'parentsNames'), tpl[:-2]+(list(map(int,tpl[-2])),)+(tpl[-1],))) 
       for tpl in zip(namesIDs, namesList, address, parentsIDsLists, parentsNamesLists)]

输出:

[{'nameIDs': 1,
  'namesList': 'jacksparrow',
  'address': 'addr1',
  'parentsIDs': [11, 12, 13, 14],
  'parentsNames': ['Teague', 'MrsT', 'gPaT', 'gMaT']},
 {'nameIDs': 2,
  'namesList': 'aragron',
  'address': 'addr2',
  'parentsIDs': [21, 22, 23, 24],
  'parentsNames': ['Arathorn', 'Gilraen', 'gPaAg', 'gMaAg']},
 {'nameIDs': 3,
  'namesList': 'harrypotter',
  'address': 'addr3',
  'parentsIDs': [31, 32, 33, 34],
  'parentsNames': ['James', 'Lily', 'gPaHp', 'gMaHp']},
 {'nameIDs': 4,
  'namesList': 'bilbo',
  'address': 'addr4',
  'parentsIDs': [41, 42, 43, 44],
  'parentsNames': ['Bungo', 'Belladona', 'gPaB', 'gMaB']},
 {'nameIDs': 5,
  'namesList': 'einstein',
  'address': 'addr5',
  'parentsIDs': [51, 52, 53, 54, 55, 56],
  'parentsNames': ['Herman', 'Pauline', 'Abraham', 'Helene', 'babyA', 'babyB']}]

【讨论】:

  • 由于某种原因,您的答案仅产生 len(out)=4,第五个元素没有被生成(在 cmd 和 VScode 中尝试)。此外,parentsIDs 和 parentsNames 列表可能更长且不匹配,因此在这种情况下使用 len(nameIDs) 进行切片将不起作用。
  • parentsNames/parentsIDs 的元素数量将始终相互匹配,但可能是任意的。即,如果您查看输出的最后一个元素,则 parentsNames/parentsIDs 有 2 个额外元素 - 我需要能够为这两个列表允许任意数量的元素
  • 基本上,parentsIDs/parentsNames 指的是一个树视图结构,其中每个 namesList/nameID 可以具有任意父级的“深度” - 所以我需要能够解释这个任意 parentID 深度 - 我可以做到这一点在我的代码中,因为 parentID 从索引 5 开始并在最后一行条目结束。我将这些 parentsID 存储在一个列表中
  • 对不起,让我解释一下,基本上元素 (41) 告诉你它是第 4 行的第一个元素,并一直持续到 indexOf(51)-1 - 即 44。
【解决方案3】:

您的具体示例展示了一种可能被利用的 parentsID 模式。每个组都在十个范围内(11..14, 21..24, ...),因此我们可以使用 groupy 对 parentsIDs 列表进行分区,并通过迭代器对具有相同分布的 parentsNames 进行分组:

from itertools import groupby,islice
d = [ {'nameIDs':ni, 'namesList':nl, 'address':ad,
       'parentIDs':pi, 'parentsNames':[*islice(pn,len(pi))] }
      for pn in [iter(parentsNames)]
      for ni,nl,ad,(pi,pi[:])
      in zip(namesIDs, namesList, address,
             groupby(parentsIDs,lambda i:[i//10]))]

输出:

[{'nameIDs': 1, 'namesList': 'jacksparrow', 'address': 'addr1', 'parentIDs': [11, 12, 13, 14], 'parentsNames': ['Teague', 'MrsT', 'gPaT', 'gMaT']},    
 {'nameIDs': 2, 'namesList': 'aragron', 'address': 'addr2', 'parentIDs': [21, 22, 23, 24], 'parentsNames': ['Arathorn', 'Gilraen', 'gPaAg', 'gMaAg']},    
 {'nameIDs': 3, 'namesList': 'harrypotter', 'address': 'addr3', 'parentIDs': [31, 32, 33, 34], 'parentsNames': ['James', 'Lily', 'gPaHp', 'gMaHp']},    
 {'nameIDs': 4, 'namesList': 'bilbo', 'address': 'addr4', 'parentIDs': [41, 42, 43, 44], 'parentsNames': ['Bungo', 'Belladona', 'gPaB', 'gMaB']},    
 {'nameIDs': 5, 'namesList': 'einstein', 'address': 'addr5', 'parentIDs': [51, 52, 53, 54, 55, 56], 'parentsNames': ['Herman', 'Pauline', 'Abraham', 'Helene', 'babyA', 'babyB']}]

显然这只是模拟数据,实际的 parentsIDs 可能无法根据 i//10 进行分组,但是,如果您有任何方法从 parentsIDs 值中获取不同的分组键,这将允许您形成分组与这些列表的所需分区相对应的任意大小

【讨论】:

  • 感谢@Alain T. 的解释,作为一个努力学习的初学者,所以对您的解决方案进行一些解释会有很长的路要走!作为一个新用户,如果我第一次错过了任何“关键”信息,这真的很糟糕——我的问题仍然有效,但被否决了——这不像我对任何人无礼或没有立即回应.我一直认为 stackoverflow 上的人会更受欢迎/更好,特别是如果他们在这里帮助新编码人员 - 但似乎更多的是要以极快的速度获得答案以获得最高分。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-04-06
  • 2018-09-18
  • 2020-09-12
  • 1970-01-01
  • 2023-02-24
  • 2019-03-12
  • 2021-12-07
相关资源
最近更新 更多