Python两个字典列表的交集

5

I have 2 lists of dicts like

list1 = [{'count': 351, 'evt_datetime': datetime.datetime(2015, 10, 23, 8, 45), 'att_value': 'red'},
     {'count': 332, 'evt_datetime': datetime.datetime(2015, 10, 23, 8, 45), 'att_value': 'red'},
     {'count': 336, 'evt_datetime': datetime.datetime(2015, 10, 23, 8, 45), 'att_value': 'red'},
     {'count': 359, 'evt_datetime': datetime.datetime(2015, 10, 23, 8, 45), 'att_value': 'red'},
     {'count': 309, 'evt_datetime': datetime.datetime(2015, 10, 23, 8, 45), 'att_value': 'red'}]


list2 = [{'count': 359, 'evt_datetime': datetime.datetime(2015, 10, 23, 8, 45), 'att_value': 'red'},
     {'count': 351, 'evt_datetime': datetime.datetime(2015, 10, 23, 8, 45), 'att_value': 'red'},
     {'count': 381, 'evt_datetime': datetime.datetime(2015, 10, 22, 8, 45), 'att_value': 'red'}]

我试图从这两个列表中获取公共字典。我的期望输出是字典的键和值都完全匹配。

[{'count': 359, 'evt_datetime': datetime.datetime(2015, 10, 23, 8, 45), 'att_value': 'red'},
     {'count': 351, 'evt_datetime': datetime.datetime(2015, 10, 23, 8, 45), 'att_value': 'red'}]

这可以由Python本身高效地完成,还是需要像pandas这样的库?
3个回答

12

使用列表推导式:

[x for x in list1 if x in list2]

这将为您的数据返回以下列表:

[{'count': 351, 'evt_datetime': datetime.datetime(2015, 10, 23, 8, 45), 'att_value': 'red'}, {'count': 359, 'evt_datetime': datetime.datetime(2015, 10, 23, 8, 45), 'att_value': 'red'}]

1
太棒了!我没想到这可以在一个简单的列表推导式中完成。运行得非常好。谢谢。 - shivg
比 for 循环好一千倍。谢谢。 - ConanG
这是一个O(n^2)的解决方案,而有一些O(n)的解决方案 - 这意味着对于1000个元素,该解决方案比使用排序列表的最优解慢1000倍。 - Wolfgang Fahl
如果列表未事先排序,请参见下面的O(n log n)解决方案。 - Wolfgang Fahl

2
下面的解决方案可能对大型列表表现更好,但由于排序步骤,可能需要更多的内存。可以通过定义sortKey(例如'count')或使用字典的哈希值来执行交集,如https://dev59.com/x2025IYBdhLWcg3wpXoe#60765557所建议的。该算法对两个列表进行排序,并并行迭代检查两个迭代器的三种可能状态:
  • 第一个迭代器在第二个迭代器后面 -> 推进第一个迭代器
  • 第二个迭代器在第一个迭代器后面 -> 推进第二个迭代器
  • 两个迭代器在同一位置 -> 找到交集元素
在给定的示例中,使用“计数”字段作为sortKey与使用字典哈希作为键得到相同的结果。
[{'count': 351, 'evt_datetime': datetime.datetime(2015, 10, 23, 8, 45), 'att_value': 'red'}, {'count': 359, 'evt_datetime': datetime.datetime(2015, 10, 23, 8, 45), 'att_value': 'red'}]
[{'count': 351, 'evt_datetime': datetime.datetime(2015, 10, 23, 8, 45), 'att_value': 'red'}, {'count': 359, 'evt_datetime': datetime.datetime(2015, 10, 23, 8, 45), 'att_value': 'red'}]

Python单元测试

def testIntersection(self):
        '''
        test creating the intersection of a list of dictionaries
        '''
        list1 = [{'count': 351, 'evt_datetime': datetime.datetime(2015, 10, 23, 8, 45), 'att_value': 'red'},
         {'count': 332, 'evt_datetime': datetime.datetime(2015, 10, 23, 8, 45), 'att_value': 'red'},
         {'count': 336, 'evt_datetime': datetime.datetime(2015, 10, 23, 8, 45), 'att_value': 'red'},
         {'count': 359, 'evt_datetime': datetime.datetime(2015, 10, 23, 8, 45), 'att_value': 'red'},
         {'count': 309, 'evt_datetime': datetime.datetime(2015, 10, 23, 8, 45), 'att_value': 'red'}]

        list2 = [{'count': 359, 'evt_datetime': datetime.datetime(2015, 10, 23, 8, 45), 'att_value': 'red'},
             {'count': 351, 'evt_datetime': datetime.datetime(2015, 10, 23, 8, 45), 'att_value': 'red'},
             {'count': 381, 'evt_datetime': datetime.datetime(2015, 10, 22, 8, 45), 'att_value': 'red'}]
        
        listi=ListOfDict.intersect(list1, list2,'count')
        print(listi)
        self.assertEquals(2,len(listi))
        listi=ListOfDict.intersect(list1, list2)
        print(listi)
        self.assertEquals(2,len(listi))

ListOfDict

'''
Created on 2020-08-11

@author: wf
'''

class ListOfDict(object):
    '''
    https://dev59.com/BpDea4cB1Zd3GeqPgczq#33543164
    '''
    @staticmethod  
    def sortKey(d,key=None):
        ''' get the sort key for the given dict d with the given key
        '''
        if key is None:
            # https://dev59.com/x2025IYBdhLWcg3wpXoe#60765557
            return hash(tuple(d.items()))
        else:
            return d[key] 
        
    @staticmethod            
    def intersect(listOfDict1,listOfDict2,key=None):
        '''
        get the  intersection lf the two lists of Dicts by the given key 
        '''
        i1=iter(sorted(listOfDict1, key=lambda k: ListOfDict.sortKey(k, key)))
        i2=iter(sorted(listOfDict2, key=lambda k: ListOfDict.sortKey(k, key)))
        c1=next(i1)
        c2=next(i2)
        lr=[]
        while True:
            try:
                val1=ListOfDict.sortKey(c1,key)
                val2=ListOfDict.sortKey(c2,key)
                if val1<val2:
                    c1=next(i1)
                elif val1>val2:
                    c2=next(i2)
                else:
                    lr.append(c1)
                    c1=next(i1)
                    c2=next(i2)
            except StopIteration:
                break     
        return lr  


    

-4
如果顺序不重要,而且您不需要担心重复项,那么您可以使用集合交集:
a = [1,2,3,4,5]
b = [1,3,5,6]
list(set(a) & set(b))
[1, 3, 5]

是的,由于dict()中包含了不可哈希(unhashable)的对象。这是我的错误。 - Rutul Raval
字典不可哈希,因此我的列表无法使用set()。 - shivg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接