【问题标题】:Compare two lists of dictionaries比较两个字典列表
【发布时间】:2017-08-21 03:26:23
【问题描述】:

假设我有两个字典列表:

a=[{'name':'A','color':'1'},
   {'name':'B','color':'2'}]
b=[{'name':'A','color':'3'},
   {'name':'c','color':'1'}]

我需要这样的东西:

for i in a:
    if i['name'] is not existent in b['name']:
         do this.
    else:
        if i['color'] is < than the corresponding item in b list:
            do that.

我不知道如何从导致迭代继续“else:”的第二个列表中获取元素。

我需要说第一个列表较小(几百个项目),但第二个列表有几千个字典,每个大约有一百个项目-效率非常重要。

我确实考虑过为两个列表中的 key['name'] 制作一个所有值的列表并进行比较,但这意味着第一次迭代以制作这些列表,然后重复列表以执行此操作或去做。 提前致谢!

【问题讨论】:

  • 如果b 中的名称是唯一的。我建议你更改b 的构造。 b={'A':{'color':'3'},'c':{'color':'1'}}。然后可以使用 b[i['name']] 来获取 {'color':'1'} 等属性。所以 b[i['name']]['color'] 是颜色的数量。使用这种结构会更有效。如果你同意,我可以给你更多的例子作为答案。
  • @Zealseeker b 中的每个元素都是一个具有约 100 个键的唯一字典,每个键都有一个值/键,其中一个键是“名称”。你说要从字典列表转换为嵌套字典。我需要考虑清楚。
  • 不要为两个列表创建一个值列表,而是只为列表 B 执行此操作。然后将 if 条件更改为 if i['name'] is not existent in names_in_b: 您需要检查此方法的性能增益,但是有了这个,你只需要对 B 进行一次迭代,然后对 A 进行迭代。
  • 您可以按名称保留仅存在于 ab 上的值,拆分为 2 个新数组。此数组之一将仅包含唯一的名称。排序另一个并zip他们。
  • @Mike 如果名称可以是每个项目的唯一索引,最好创建一个嵌套字典,因为这样可以快速搜索。此外,您还可以轻松创建仅保留项目索引的字典。例如:{'A': 0, 'c': 1} 这样会更快。

标签: python list dictionary


【解决方案1】:

先做一个索引:

a=[{'name':'A','color':'1'},
   {'name':'B','color':'2'}]

b=[{'name':'A','color':'3'},
   {'name':'C','color':'1'}]

dic = {}
for i,item in enumerate(b):
    dic[item['name']] = i
# dic will be {'A':0,'C':1}
for item in a:
    if not item['name'] in dic:
        #do this
    else:
        if b[dic[item['name']]]['color'] > item['color']:
            #do that

【讨论】:

  • 为什么不直接在b 中存储对字典的引用,而不是它们的索引?
  • @ArthurTacca 是的,我看到了你的代码,很好。但我认为(虽然可能不是真的)直接存储所有数据可能会浪费内存并降低性能?
  • 不,这不是变量在 Python 中的工作方式;只存储引用。
  • @ArthurTacca 好的,谢谢。当我对 OP 发表评论时,我确实建议 OP 将数据存储在字典中。而且这种替代方式可能更容易理解 :grin:
【解决方案2】:

您可以使用哈希表或类似的东西:

a=[{'name':'A','color':'1'},
   {'name':'B','color':'2'}]
b=[{'name':'A','color':'3'},
   {'name':'c','color':'1'}]

for item in a:
    mach = list(filter(lambda x: x['name'] == item['name'], b))
    if mach:
        if int(item['color']) > int(mach[0]['color']):
            do that
    else:
        do this

Dict in Python is a kind of hash tableamortized O(1)。 然后你可以把你的代码改成这样:

a=[{'name':'A','color':'1'},
   {'name':'B','color':'2'}]
b=[{'name':'A','color':'3'},
   {'name':'c','color':'1'}]

b_dict = {item['name']:item['color'] for item in b}
for item in a:
    if item['name'] in b_dict and int(item['color']) < int(b_dict[item['name']]):
        print 'do this'
    else:
        print 'do that'

【讨论】:

  • 这是n*n。由于 OP 提到列表非常大,因此此解决方案无法扩展!
  • 是的,但是你有两个列表,你必须搜索每个项目,然后你的表现~= O(n^2)
  • 您不必每次都浏览这两个列表
  • @RaminNietzsche 这就是整个“优化”和“可扩展性”存在的原因
  • @RaminNietzsche 如果名称在第二个列表的每个项目中都是唯一的,则它是可散列的。
【解决方案3】:

不要为两个列表创建一个值列表,而是只为列表 B 执行此操作。然后将您的 if 条件更改为

if i['name'] is not existent in names_in_b:

您需要检查此方法的性能增益,但使用此解决方案,您只需对 B 进行一次迭代,然后对 A 进行迭代。

【讨论】:

    【解决方案4】:

    您绝对希望在开始之前对b 进行迭代。唯一明显的替代方法是为a 中的每个 项迭代b,这显然更糟。

    b_dict = {x['name']: x for x in b}
    for item in a:
        if item['name'] in b_dict:
            f(b_dict['name']) 
        else:
            pass  # whatever
    

    您可能对 Python 字典的 get() 方法感兴趣,如果您希望避免使用 in 并立即获取元素。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-23
      • 1970-01-01
      • 1970-01-01
      • 2021-05-26
      • 2012-02-21
      • 1970-01-01
      相关资源
      最近更新 更多