如何在Python中搜索元组列表


90

所以我有一个这样的元组列表:

[(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]

我希望此列表包含一个数字值等于某值的元组。

因此,如果我这样做 search(53),它将返回索引值2

是否有捷径可寻?

Answers:


94
[i for i, v in enumerate(L) if v[0] == 53]

68
你能解释一下吗?
schatten

17
用词解释:对于L的枚举列表中的每个i,v(这使i成为枚举列表中的元素,v成为原始元组),检查元组的第一个元素是否为53,如果是,则附加代码结果在“ for”到新创建的列表之前,请在此处:i。也可以是my_function(i,v)或另一个列表理解。由于您的元组列表只有一个元组,第一个值为53,因此您将获得一个包含一个元素的列表。
djangonaut 2014年

6
如果v [0] == 53] .pop()具有int值,我只会在enumerate(L)中添加[i for i,v。
alemol '16

49

您可以使用列表推导

>>> a = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]
>>> [x[0] for x in a]
[1, 22, 53, 44]
>>> [x[0] for x in a].index(53)
2

47

tl; dr

一个生成器表达式可能是最高效和简单的解决问题的方法:

l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]

result = next((i for i, v in enumerate(l) if v[0] == 53), None)
# 2

说明

有几个答案可以通过列表理解为该问题提供简单的解决方案。尽管这些答案是完全正确的,但它们并不是最佳的。根据您的用例,进行一些简单的修改可能会带来很多好处。

我在此用例中使用列表理解所遇到的主要问题是,尽管您只想查找1个元素,但将处理整个列表。

Python提供了一个简单的结构,在这里非常理想。它称为生成器表达式。这是一个例子:

# Our input list, same as before
l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]

# Call next on our generator expression.
next((i for i, v in enumerate(l) if v[0] == 53), None)

在我们的琐碎示例中,我们可以期望这种方法与列表推导方法基本相同,但是如果使用更大的数据集该怎么办?这就是使用生成器方法的优势发挥作用的地方。与其构建新列表,不如使用您现有的列表作为迭代对象,并使用next()从生成器中获取第一项。

让我们看一下这些方法在某些较大的数据集上的表现如何不同。这些是大型列表,由10000000 +1个元素组成,目标是开始(最佳)或结束(最差)。我们可以使用以下列表理解来验证这两个列表的性能是否相同:

清单理解

“最糟糕的情况”

worst_case = ([(False, 'F')] * 10000000) + [(True, 'T')]
print [i for i, v in enumerate(worst_case) if v[0] is True]

# [10000000]
#          2 function calls in 3.885 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         1    3.885    3.885    3.885    3.885 so_lc.py:1(<module>)
#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

“最好的情况”

best_case = [(True, 'T')] + ([(False, 'F')] * 10000000)
print [i for i, v in enumerate(best_case) if v[0] is True]

# [0]
#          2 function calls in 3.864 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         1    3.864    3.864    3.864    3.864 so_lc.py:1(<module>)
#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

生成器表达式

这是我对生成器的假设:我们将看到,在最佳情况下,生成器的性能将显着提高,但在最坏情况下,生成器的性能也将类似。这种性能提升主要是由于生成器被延迟评估的事实所致,这意味着生成器将仅计算产生值所需的内容。

最糟糕的情况

# 10000000
#          5 function calls in 1.733 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         2    1.455    0.727    1.455    0.727 so_lc.py:10(<genexpr>)
#         1    0.278    0.278    1.733    1.733 so_lc.py:9(<module>)
#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
#         1    0.000    0.000    1.455    1.455 {next}

最好的情况

best_case  = [(True, 'T')] + ([(False, 'F')] * 10000000)
print next((i for i, v in enumerate(best_case) if v[0] == True), None)

# 0
#          5 function calls in 0.316 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         1    0.316    0.316    0.316    0.316 so_lc.py:6(<module>)
#         2    0.000    0.000    0.000    0.000 so_lc.py:7(<genexpr>)
#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
#         1    0.000    0.000    0.000    0.000 {next}

什么?!最好的情况吹走了列表的理解力,但是我没想到我们最坏的情况会在一定程度上胜过列表的理解力。那个怎么样?坦白说,我只能推测,无需进一步研究。

一粒盐地拿走所有这些,我这里没有进行任何可靠的分析,只是一些非常基本的测试。这应该足以理解生成器表达式对于这种类型的列表搜索更有效。

请注意,这都是基本的内置python。我们不需要导入任何东西或使用任何库。

我首先在Peter Norvig 的Udacity cs212课程中看到了这项搜索技术。


2
有趣,我测试并发现它真的很快
Grijesh Chauhan 2014年

3
这应该是公认的答案。生成器表达式在运行时不会具体化整个输出序列,而是将其求值为一个迭代器,该迭代器一次可从该表达式中产生一项。
BoltzmannBrain

2
这很棒,比我的列表理解要快得多,谢谢!
mindm49907

29

您的元组基本上是键-值对-一个dictpython-因此:

l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]
val = dict(l)[53]

编辑-啊哈,您说您想要索引值为(53,“ xuxa”)。如果这确实是您想要的,则必须遍历原始列表,或者制作更复杂的字典:

d = dict((n,i) for (i,n) in enumerate(e[0] for e in l))
idx = d[53]

2
如果我们忽略了OP的实际要求,我想您的最初答案就是“如何在Python中搜索元组列表”的最佳答案
Rick Westera

您的第一个答案对我而言很有用。不过,最好使用.get(),以防该项目不在字典中。 l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")] val = dict(l).get(53)
user1503941 2015年

12

嗯...好吧,想到的简单方法就是将其转换为字典

d = dict(thelist)

和访问d[53]

编辑:糟糕,第一次误读您的问题。听起来您实际上想要获取存储给定数字的索引。在这种情况下,请尝试

dict((t[0], i) for i, t in enumerate(thelist))

而不是简单的旧dict转换。然后d[53]是2。


6

假设列表可能很长且数字可能重复,请考虑使用Python sortedcontainers模块中SortedList类型。SortedList类型将自动按数字顺序维护元组,并允许快速搜索。

例如:

from sortedcontainers import SortedList
sl = SortedList([(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")])

# Get the index of 53:

index = sl.bisect((53,))

# With the index, get the tuple:

tup = sl[index]

通过执行二进制搜索,这将比列表理解建议快得多。字典建议仍然会更快,但如果可能存在带有不同字符串的重复数字,则字典建议将不起作用。

如果重复的数字使用不同的字符串,则您需要再执行一步:

end = sl.bisect((53 + 1,))

results = sl[index:end]

通过平分54,我们将找到切片的结束索引。与接受的答案相比,这在长列表上将明显更快。



-1

[如果v ==' delicia ' ,则k为l中的k,v

这里l是元组列表-[(1,“ juca”),(22,“ james”),(53,“ xuxa”),(44,“ delicia”)]

而且,我们没有将其转换为字典,而是使用了llist理解。

*Key* in Key,Value in list, where value = **delicia**


是的,当然。谢谢@cosmoonot。
Mantej Singh

这里l是元组的列表-[(1,“ juca”),(22,“ james”),(53,“ xuxa”),(44,“ delicia”)]并且不是将其转换为字典,我们正在使用llist理解。` 在键,值列表,其中value = delicia `
Mantej辛格
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.