Как искать список кортежей в Python

Итак, у меня есть список кортежей, например:

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

Мне нужен этот список для кортежа, число которого равно чем-то.

Итак, если я сделаю search(53), он вернет значение индекса 2

Есть ли простой способ сделать это?

Ответ 1

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

Ответ 2

Вы можете использовать понимание :

>>> 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

Ответ 3

TL;DR

A выражение генератора, вероятно, является наиболее эффективным и простым решением вашей проблемы:

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}

ЧТО?! Лучший случай сбрасывает понимание списков, но я не ожидал, что наш худший случай превзойдет переписку в такой степени. Как так? Честно говоря, я мог только спекулировать без дальнейших исследований. Возможно, я попрошу об этом.

Возьмите все это с кусочком соли, я не провел никакого прочного профилирования здесь, просто некоторые очень простые тесты. Я хотел бы включить некоторое профилирование памяти, но эти методы убегают от меня. Однако то, что мы видим здесь, достаточно, чтобы увидеть, что выражение для генератора более эффективно для поиска этого типа в списке.

Обратите внимание, что это все базовый, встроенный питон. Нам не нужно ничего импортировать или использовать специальные библиотеки.

И чтобы дать кредит, когда кредит должен быть, я впервые увидел эту технику в свободном курсе, Udacity cs212 с Питером Норвигом.

Ответ 4

Ваши кортежи в основном представляют собой пары ключ-значение - питон dict - так:

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]

Ответ 5

Хм... ну, простой способ, который приходит на ум, - превратить его в dict

d = dict(thelist)

и доступ d[53].

РЕДАКТИРОВАТЬ: Ой, неправильно спросите свой вопрос в первый раз. Похоже, вы действительно хотите получить индекс, в котором хранится заданное число. В этом случае попробуйте

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

вместо простого старого преобразования dict. Тогда d[53] будет 2.

Ответ 6

Предположим, что список может быть длинным, и числа могут повторяться, рассмотрите использование SortedList из сортированных контейнеров Python модуль. Тип 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 мы найдем конечный индекс для нашего среза. Это будет значительно быстрее в длинных списках по сравнению с принятым ответом.

Ответ 7

Еще один способ.

zip(*a)[0].index(53)

Ответ 8

[k для k, v в l, если v == ' delicia']

здесь l - список кортежей - [(1, "juca" ), (22, "james" ), (53, "xuxa" ), (44, "delicia" )]

И вместо того, чтобы преобразовать его в dict, мы используем llist-понимание.

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