我正在寻找一种Pythonic方式或更有效的方法来解决这个问题。我有一个字典,其中设置为值(跨键允许重复)。给定一个列表,我必须创建一个字典,使用主字典中的键将每个类别映射到元素。我举一个例子来说明。
大师词典
{
"KeyA": ['Aron', 'Ranom Value', 'Abhishek'],
"KeyB": ['Ball', 'Foo', 'Bar', 'Badge', 'Dog'],
"KeyZ": ['Random Value', 'Foo', 'Bar']
}
输入
['Foo', 'Bar', 'Dog', 'Aron']
输出
{
"KeyA": ['Aron'],
"KeyB": ['Bar', 'Foo', 'Dog'],
"KeyZ": ['Foo', 'Bar']
}
将组中的各个项目反转为键,然后进行查找。
{
'Aron' : ['KeyA'],
'Foo' : ['KeyB', 'KeyZ'],
'Bar' : ['KeyB', 'KeyZ'],
'Random Value' : ['KeyA', 'KeyZ']
}
我通过浏览每一组中的每一项来初始化倒置字典。创建这样的字典的大约时间是O(n)。在如此创建的反向字典中查找列表中的项目。说出值Bar
。使用信息'Bar': ['KeyB', 'KeyZ']
创建新词典。结果字典为{'KeyB': ['Bar'], 'KeyZ': ['Bar']}
。对于下一个项目,我必须对现有字典进行一些记账,如密钥是否存在,如果是,则附加到现有列表,依此类推。
使用映射的集合中的in运算符(检查成员资格)到每个键
主词典和输入列表在大多数时候都会非常小。 (所有套装中的独特物品少于500件)。所以我可以检查每个键返回的集合的成员资格并创建一个字典。这显然效率较低,但适用于大多数情况。
我还有一些类似于上面给出的例子的操作。我不想为所有人进行手动记账,因为他们容易出错并且比内置功能慢。
我需要什么?
答案 0 :(得分:5)
在开始转换之前将列表转换为集合怎么样?集合查找比列表中的线性搜索更快。
input_set = set(input)
一旦你拥有它,你可以使用常规字典理解,在我看来:
output = {key: [x for x in value if x in input_set] for key, value in master_dict.items()}
结果:
output == {'KeyB': ['Foo', 'Bar', 'Dog'], 'KeyA': ['Aron'], 'KeyZ': ['Foo', 'Bar']}
答案 1 :(得分:4)
一种方法是在python中使用cross,如下所示:
x={
"KeyA": ['Aron', 'Ranom Value', 'Abhishek'],
"KeyB": ['Ball', 'Foo', 'Bar', 'Badge', 'Dog'],
"KeyZ": ['Random Value', 'Foo', 'Bar']
}
items = ['Foo', 'Bar', 'Dog', 'Aron']
{k: set(items).intersection(set(v)) for k, v in x.items()}
答案 2 :(得分:1)
使用defaultdict和list comprehension。
from collections import defaultdict
result = defaultdict(list)
d = {
"KeyA": ['Aron', 'Ranom Value', 'Abhishek'],
"KeyB": ['Ball', 'Foo', 'Bar', 'Badge', 'Dog'],
"KeyZ": ['Random Value', 'Foo', 'Bar']
}
items = ['Foo', 'Bar', 'Dog', 'Aron']
[result[k].append(e) for k,v in d.items() for e in v if e in items]
print(result) # defaultdict(<type 'list'>, {'KeyB': ['Foo', 'Bar', 'Dog'], 'KeyA': ['Aron'], 'KeyZ': ['Foo', 'Bar']})
print(dict(result)) # {'KeyB': ['Foo', 'Bar', 'Dog'], 'KeyA': ['Aron'], 'KeyZ': ['Foo', 'Bar']}
答案 3 :(得分:1)
另一种可能的方法:
可能加快检查input_set
中是否存在值的搜索时间的一种方法是使用二进制搜索,即O(logn)
。
以下是一些示例代码,它也使用了方便的collections.defaultdict
:
from collections import defaultdict
master = {
"KeyA": ['Aron', 'Ranom Value', 'Abhishek'],
"KeyB": ['Ball', 'Foo', 'Bar', 'Badge', 'Dog'],
"KeyZ": ['Random Value', 'Foo', 'Bar']
}
input_set = ['Foo', 'Bar', 'Dog', 'Aron']
sorted_list = sorted(input_set)
d = defaultdict(list)
for key, value in master.items():
for v in value:
if binary_search(sorted_list, v):
d[key].append(v)
print(d)
哪个输出:
defaultdict(<class 'list'>, {'KeyA': ['Aron'], 'KeyB': ['Foo', 'Bar', 'Dog'], 'KeyZ': ['Foo', 'Bar']})
下面定义了binary_search()
:
def binary_search(item_list,item):
first = 0
last = len(item_list)-1
while first <= last:
mid = (first + last)//2
if item_list[mid] == item :
return True
elif item < item_list[mid]:
last = mid - 1
else:
first = mid + 1
return False
上面的代码似乎重新发明了轮子。您可以查看bisect
模块,它提供了一些调用二进制搜索的方法,而无需编写自己的函数。
注意:为了使用二进制搜索,您还需要预先对值进行排序,即O(nlogn)
。我不完全确定这将产生多大的影响,你必须用另一种方法进行一些测试来看看差异。
此外,正如@SuperSaiyan发布的那样,将input_set
转换为集合是最有效的方法,因为在最好的情况下设置查找O(1)
,在最坏的情况下设置O(n)
(罕见) )。
答案 4 :(得分:1)
OP提议reverse dictionary。它可以说仍然是pythonic,所以这里是如何实现的。
<强>鉴于强>
import collections as ct
master_dict = {
"KeyA": ['Aron', 'Random Value', 'Abhishek'],
"KeyB": ['Ball', 'Foo', 'Bar', 'Badge', 'Dog'],
"KeyZ": ['Random Value', 'Foo', 'Bar']
}
input_list = ['Foo', 'Bar', 'Dog', 'Aron']
<强>代码强>
我们使用collections.defaultdict
来简化列表值的创建。
reverse_dict = ct.defaultdict(list)
for k, v in master_dict.items():
for item in v:
reverse_dict[item].append(k)
reverse_dict
输出
defaultdict(list,
{'Abhishek': ['KeyA'],
'Aron': ['KeyA'],
'Badge': ['KeyB'],
'Ball': ['KeyB'],
'Bar': ['KeyB', 'KeyZ'],
'Dog': ['KeyB'],
'Foo': ['KeyB', 'KeyZ'],
'Random Value': ['KeyA', 'KeyZ']})
现在可以通过键搜索输入,查找比搜索每个字符串列表更快。我们从查找值的输入列表构建最终字典。
final_dict = ct.defaultdict(list)
for v in input_list:
for k in reverse_dict[v]:
final_dict[k].append(v)
final_dict
输出
defaultdict(list,
{'KeyA': ['Aron'],
'KeyB': ['Foo', 'Bar', 'Dog'],
'KeyZ': ['Foo', 'Bar']})
@SuperSaiyan建议通过搜索输入列表的集来重建主词典的每个键的列表。对于这种特殊应用,这是一种出色而卓越的方法。