【问题标题】:Efficient way to find key by value in Python dict where dict values are iterables在 Python dict 中按值查找键的有效方法,其中 dict 值是可迭代的
【发布时间】:2021-12-18 10:40:15
【问题描述】:

我有一个可迭代的唯一数字:

lst = [14, 11, 8, 55]

其中每个值都在 dict 的可迭代值中,比如列表:

dict_itms.items() = dict_items([(1, [0, 1, 2, 3]), (2, [11, 14, 12]), (3, [30, 8, 42]), (4, [55, 6])])

我必须在 dict 中找到每个 lst 元素,最后,我将拥有一个针对 lst 中每个元素的成对键列表。

这个方法:

keys_ = []
for a in lst:
    for k, v in dict_itms.items():
        if a in v:
            keys_ += [k]
            break
        else:
            continue

给出: [2, 2, 3, 4]

有没有更有效的方法可以针对每个要查找的数字逐对查找每个密钥?

【问题讨论】:

  • 据我了解,问题是查看两个列表是否共享任何元素。请参阅:*.com/a/17735466/2681662。这样你就可以摆脱一个循环(第一个循环)
  • 所有列表编号都是唯一的

标签: python dictionary key pairwise


【解决方案1】:

您可以在列表理解中使用any

print([k for k,v in dict_itms.items() if any(x in lst for x in v)])

输出:

[2, 3, 4]
更新

据此answernot set(v).isdisjoint(lst)是最快的:

print([k for k,v in dict_itms.items() if not set(v).isdisjoint(lst)])

【讨论】:

  • 谢谢,但看起来效率不高
  • 尝试将any() 替换为not set(v).isdisjoint(lst)。在你的情况下效率更高吗?
  • 我明白了,但是需要“针对每个要查找的数字查找每个键”,即,列表中必须有一个针对每个要查找的值的键: len([2, 2 , 3, 4]) == len(lst)。这也与 Grismar 的回答有关
  • 鉴于这些要求,我不知道如何进一步改进您的解决方案。
【解决方案2】:

不清楚您所说的“高效”是什么意思;您是否需要在 given 通行证或 聚合 中提高效率?我问的原因是,通常处理这个问题的最好方法是通过预处理传递来翻转你的键值关系:

reverse_lookup = dict()
for k,v in d.items():
  for i in v:
    keys = reverse_lookup.get(i, [])  # Provide an empty list if this item not yet found
    keys.append(k)
    reverse_lookup[i] = keys

现在您已经处理了反向查找,您可以直接使用它:

result = [reverse_lookup.get(i) for i in lst]
# `result` is actually a list of lists, so as to allow duplicates. You will need to flatten it, or change the reverse lookup to ignore dupes.

反向查找的初始处理是O(n*m),其中n*m 是原始字典值相加的总长度。但是,lst 部分的每次查找都是 O(1),所以如果你眯着眼睛并且有足够的查找次数,这是O(p),其中plst 的长度。如果您必须经常这样做,这将比其他方法更有效,而如果您只传递给定字典一次,则效率会低得多。

【讨论】:

  • 当然,总体效率很高,谢谢,在最初的字典创建阶段翻转字典是个好主意。由于我们可以随意构建 2d 数据(在此处以字典形式呈现),您会选择什么?我想在这里编写 2d numpy 数组并使用行索引作为 dict 键,但是 np.argwhere(np.isin(d, lst))[:,0] 不适用于参差不齐的数组。
【解决方案3】:

一个简单的 Pythonic 实现:

d = dict([(1, [0, 1, 2, 3]), (2, [11, 14, 12]), (3, [30, 8, 42]), (4, [55, 6])])

xs = [14, 11, 8, 55]

keys = [k for k, v in d.items() if set(v).intersection(xs)]
print(keys)

但是,这不会复制您的示例中的 2 键 - 不确定这是否是您需要的行为?

【讨论】:

  • 看起来更好,谢谢,效率更高,但不确定是否足够。等待一段时间