Python:比较两个dict列表

时间:2014-02-07 09:54:29

标签: python dictionary

这是我想要实现的目标:

我有两个词典列表。所有词典都具有以下结构:

dictinary = {'name':'MyName', 'state':'MyState'}

我想浏览两个列表的所有元素,并比较具有相同名称的条目的状态。这是我能想象到的最佳方式:

for d in list1:
    name = d['name']
    for d2 in list2:
        if d2['name'] == name:
           if d1['state'] != d2['state']:
               # Do something

虽然我认为这种方法可行,但我想知道是否有更高效和/或更优雅的方式来执行此操作。谢谢你的想法!

3 个答案:

答案 0 :(得分:2)

从itertools看一下product

import itertools

xs = range(1,10)
ys = range(11,20)

zs = itertools.product(xs,ys)

list(zs)

[(1,11),(1,12),(1,13),(1,14),(1,15),(1,16),(1,17),(1,18) ),(1,19),(2,11),(2,12),(2,13),(2,14),(2,15),(2,16),(2,17), (2,18),(2,19),(3,11),(3,12),(3,13),(3,14),(3,15),(3,16),(3) ,17),(3,18),(3,19),(4,11),(4,12),(4,13),(4,14),(4,15),(4,16) ),(4,17),(4,18),(4,19),(5,11),(5,12),(5,13),(5,14),(5,15), (5,16),(5,17),(5,18),(5,19),(6,11),(6,12),(6,13),(6,14),(6) ,15),(6,16),(6,17),(6,18),(6,19),(7,11),(7,12),(7,13),(7,14) ),(7,15),(7,16),(7,17),(7,18),(7,19),(8,11),(8,12),(8,13), (8,14),(8,15),(8,16),(8,17),(8,18),(8,19),(9,11),(9,12),(9) ,13),(9,14),(9,15),(9,16),(9,17),(9,18),(9,19)]

其他一些事情 -

  1. 当你只代表两件事时,通常使用一个元组(甚至是一个命名的元组) 所以要考虑为什么他们开始时的决定 - 你可能有一个很好的理由:)
  2. [('name','state'),('name','state'),('name','state')...]

    另一种方法是直接比较元素,例如你可以检查setA(dicts列表1)和setB(dicts列表2)的交集

    >>> listA = [('fred','A'), ('bob','B'), ('mary', 'D'), ('eve', 'E')]
    >>> listB = [('fred','X'), ('clive', 'C'), ('mary', 'D'), ('ben','B')]
    # your listA and listB could be sets to begin with
    >>> set.intersection(set(listA),set(listB)) 
    set([('mary', 'D')])
    

    然而,这种方法不允许重复......

答案 1 :(得分:1)

我能想到的最优雅的方式是列表理解。

[[do_something() for d1 in list1 if d1["name"] == d2["name"] and d1["state"] != d2["state"]] for d2 in list2]

但那是相同的代码。

您还可以通过减少它来使示例代码更加优雅:

for d in list1:
    for d2 in list2:
        if d2['name'] == d['name'] and d['state'] != d2['state']:
            # Do something

答案 2 :(得分:1)

其他答案是有效的(它们提供了正确的答案),但是对于大型列表不能很好地执行,因为它们使用嵌套迭代 - 对于长度为N的列表,它们使用的步骤数增加如N ^ 2。如果清单很小,这不是一个问题;但如果列表很大,迭代次数就会爆炸。

保持时间复杂度与N呈线性关系的另一种方法就是这样(非常详细):

##
## sample data
data = list()
data.append( [
    dict(name='a', state='0'),
    dict(name='b', state='1'),
    dict(name='c', state='3'),
    dict(name='d', state='5'),
    dict(name='e', state='7'),
    dict(name='f', state='10'),
    dict(name='g', state='11'),
    dict(name='h', state='13'),
    dict(name='i', state='14'),
    dict(name='l', state='19'),
    ])
data.append( [
    dict(name='a', state='0'),
    dict(name='b', state='1'),
    dict(name='c', state='4'),
    dict(name='d', state='6'),
    dict(name='e', state='8'),
    dict(name='f', state='10'),
    dict(name='g', state='12'),
    dict(name='j', state='16'),
    dict(name='k', state='17'),
    dict(name='m', state='20'),
    ])

##
## coalesce lists to a single flat dict for searching
dCombined = {}
for d in data:
    dCombined = { i['name'] : i['state'] for i in d }

##
## to record mismatches
names = []

##
## iterate over lists -- individually / not nested
for d in data:
    for i in d:
        if i['name'] in dCombined and i['state'] != dCombined[i['name']]:
            names.append(i['name'])

##
## see result
print names

注意事项:

OP没有说清单中是否有重复的名字;这会改变这种方法。

根据“做某事”的细节,您可能会记录除名称之外的其他内容 - 可以存储对单个dict对象的引用或副本,或者“做某事”所需的任何内容。

这种方法的权衡是它需要比以前的答案更多的内存;但是内存需求只能与实际不匹配的数量相关,而且是O(N)。

注意:

当您有两个以上的列表要比较时,这种方法也有效 - 例如如果有5个列表,我的替代方案仍然是O(N)的时间和记忆,而之前的答案将是O(N ^ 5)及时!