I'd do something like this:
[(i, colour.index(c))
for i, colour in enumerate(colours)
if c in colour]
This will return a list of tuples where the first index is the position in the first list and second index the position in the second list (note: c
is the colour you're looking for, that is, "#660000"
).
For the example in the question, the returned value is:
[(0, 0)]
If you just need to find the first position in which the colour is found in a lazy way you can use this:
next(((i, colour.index(c))
for i, colour in enumerate(colours)
if c in colour),
None)
This will return the tuple for the first element found or None
if no element is found (you can also remove the None
argument above in it will raise a StopIteration
exception if no element is found).
Edit: As @RikPoggi correctly points out, if the number of matches is high, this will introduce some overhead because colour
is iterated twice to find c
. I assumed this to be reasonable for a low number of matches and to have an answer into a single expression. However, to avoid this, you can also define a method using the same idea as follows:
def find(c):
for i, colour in enumerate(colours):
try:
j = colour.index(c)
except ValueError:
continue
yield i, j
matches = [match for match in find('#660000')]
Note that since find
is a generator you can actually use it as in the example above with next
to stop at the first match and skip looking further.
Best Answer
See the documentation for the built-in
.index()
method of the list:Caveats
Linear time-complexity in list length
An
index
call checks every element of the list in order, until it finds a match. If the list is long, and if there is no guarantee that the value will be near the beginning, this can slow down the code.This problem can only be completely avoided by using a different data structure. However, if the element is known to be within a certain part of the list, the
start
andend
parameters can be used to narrow the search.For example:
The second call is orders of magnitude faster, because it only has to search through 10 elements, rather than all 1 million.
Only the index of the first match is returned
A call to
index
searches through the list in order until it finds a match, and stops there. If there could be more than one occurrence of the value, and all indices are needed,index
cannot solve the problem:Instead, use a list comprehension or generator expression to do the search, with
enumerate
to get indices:The list comprehension and generator expression techniques still work if there is only one match, and are more generalizable.
Raises an exception if there is no match
As noted in the documentation above, using
.index
will raise an exception if the searched-for value is not in the list:If this is a concern, either explicitly check first using
item in my_list
, or handle the exception withtry
/except
as appropriate.The explicit check is simple and readable, but it must iterate the list a second time. See What is the EAFP principle in Python? for more guidance on this choice.