如何比较多个字典列表中的键值?

3 投票
3 回答
1134 浏览
提问于 2025-04-18 16:09

我有一个字典的列表,这些字典的结构都是一样的。比如说:

test_data = [{'id':1, 'value':'one'}, {'id':2, 'value':'two'}, {'id':3, 'value':'three'}]

我需要做的是比较这些字典,并根据某个键值对返回“相似”的字典。举个例子,给定键 value 和值 oen,我想找到所有与 oen 相似的字典,在这个例子中,结果会是 [{'id':1, 'value':'one'}]

difflib 里有一个函数 get_close_matches,这个函数很接近我需要的功能。我可以用 列表推导式 来提取特定键的值,然后把这些值和我的搜索进行比较:

values = [ item['value'] for item in test_data ]
found_vals = get_close_matches('oen', values) #returns ['one']

我希望这个过程再进一步,把所有的内容和原始字典联系起来:

In  [1]: get_close_dicts('oen', test_data, 'value')
Out [1]: [{'id':1, 'value':'one'}]

注意:这个字典列表非常大,所以我希望能尽可能高效和快速。

3 个回答

0

无论如何,你最终都会需要遍历每一个字典,这是无法避免的。不过,你可以在一个预处理阶段把所有的工作做好,这样在真正调用函数的时候就能立刻得到结果。

正如ValAyal提到的,使用一个反向查找字典是个不错的主意。我想象一个字典叫做 value_dict,其中 key 是第一个字典中的值,而 value 则包含了所有完全匹配和相似匹配的值。以 d1d2 为例,它们是在你想要搜索的列表中。如果

d1 = {'id':1, 'value':'one'}
d2 = {'id':3, 'value':'oen'}

那么:

value_dict["one"] = {"exact": [d1], "close": [d2]}
value_dict["oen"] = {"exact": [d2], "close": [d1]}

每当你插入一个已经出现过的值的字典时,你可以立即找到所有的完全匹配和相似匹配(只需查找那个值),然后相应地添加到不同的列表中。如果你有一个新的值是之前没有见过的,你就需要把它和当前 value_dict 中的所有值进行比较。例如,如果你想添加

d3 = {'id':5, 'value':'one'}

你会查找 value_dict["one"],并得到完全匹配和相似匹配的列表。这些列表包含了你需要修改的所有其他 value_dict 条目。你需要把 one 的完全匹配和 oen 的相似匹配都添加进去;这两个值你可以从返回的列表中获取。最后你得到的是

value_dict["one"] = {"exact": [d1, d3], "close": [d2]}
value_dict["oen"] = {"exact": [d2], "close": [d1, d3]}

所以一旦所有的预处理完成,你的函数就变得简单了:像 get_close_dicts(val) 这样的函数(我不知道你例子中的第三个参数是干嘛的)可以直接执行 return value_dict[val]["exact"] + value_dict[val]["close"]。现在你有了一个能立刻给出答案的函数。

预处理这一步比较复杂,但希望在 get_close_dicts 中带来的速度提升能够弥补这一点。如果你想知道如何实现这个,我可以在下班后详细讲讲。希望这能给你一个有用的数据结构的好主意,我没有想得太复杂。

0

你可以这样做:

return [d for d in test_data if get_close_matches('oen', [d['value'])]]

要注意,get_close_matches 这个函数可能会返回多个结果。

2

你可以在运行 get_close_dicts 之前,先创建一个反向查找字典。这样一来,当你得到一组返回值后,就可以用这些值来查找相关的字典。

如果你能保证在字典中 'value' 这个键的值是唯一的,那么你可以这样做:

reverselookup = {thedict['value']:thedict for thedict in test_data}

但是,如果你需要处理多个字典中 'value' 键的值可能相同的情况,那么你需要把所有这些值都映射出来(这样你就会得到一个字典,键是 'value' 中的值,值是包含该值的字典列表):

from collections import defaultdict
reverselookup = defaultdict(list)
for testdict in test_data:
    reverselookup[testdict['value']].append(testdict)

举个例子,如果你的测试数据中有一个额外的字典,像这样:

>>> test_data = [{'id':1, 'value':'one'}, {'id':2, 'value':'two'}, 
                 {'id':3, 'value':'three'}, {'id':4, 'value':'three'}]

那么上面的反向查找构造会给你这个结果:

{
  "three": [
    {
      "id": 3,
      "value": "three"
    },
    {
      "id": 4,
      "value": "three"
    }
  ],
  "two": [
    {
      "id": 2,
      "value": "two"
    }
  ],
  "one": [
    {
      "id": 1,
      "value": "one"
    }
  ]
}

在你得到这些值之后,只需提取字典(如果你有列表的列表的情况,可以链式调用,如果是第一种情况就不需要链式调用):

from itertools import chain    
chain(*[reverselookup[val] for val in found_vals])

撰写回答