I want to find the index of the n\'th occurrence of an item in a list. e.g.,
x=[False,True,True,False,True,False,True,False,False,False,True,False,True]
A solution that first creates a list object and returns the nth-1 element of this list : function occurence()
And a solution that fulfill functional programmers'dreams too, I think, using generators, because I love them : function occur()
S = 'stackoverflow.com is a fantastic amazing site'
print 'object S is string %r' % S
print "indexes of 'a' in S :",[indx for indx,elem in enumerate(S) if elem=='a']
def occurence(itrbl,x,nth):
return [indx for indx,elem in enumerate(itrbl)
if elem==x ][nth-1] if x in itrbl \
else None
def occur(itrbl,x,nth):
return (i for pos,i in enumerate(indx for indx,elem in enumerate(itrbl)
if elem==x)
if pos==nth-1).next() if x in itrbl\
else None
print "\noccurence(S,'a',4th) ==",occurence(S,'a',4)
print "\noccur(S,'a',4th) ==",occur(S,'a',4)
result
object S is string 'stackoverflow.com is a fantastic amazing site'
indexes of 'a' in S : [2, 21, 24, 27, 33, 35]
occur(S,'a',4th) == 27
occurence(S,'a',4th) == 27
The second solution seems complex but it isn't really. It doesn't need to run completely through the iterable: the process stops as soon as the wanted occurence is found.
if efficiency is a concern i think its better to iterate the normally ( O(N) ) instead of list comprehension which takes O(L) where L is length of list
Example : Consider a very huge list and you want to find the first occurence N=1 it is obviously better to stop as soon as you find the first occurence
count = 0
for index,i in enumerate(L):
if i:
count = count + 1
if count==N:
return index
I think this should work.
def get_nth_occurrence_of_specific_term(my_list, term, n):
assert type(n) is int and n > 0
start = -1
for i in range(n):
if term not in my_list[start + 1:]:
return -1
start = my_list.index(term, start + 1)
return start
You could use count:
from itertools import count
x = [False, True, True, False, True, False, True, False, False, False, True, False, True]
def nth_index(n, item, iterable):
counter = count(1)
return next((i for i, e in enumerate(iterable) if e == item and next(counter) == n), -1)
print(nth_index(3, True, x))
Output
4
The idea is that due to the short-circuit nature of e == item and next(counter) == n)
, the expression next(counter) == n
only gets evaluated when e == item
so you are only counting the elements equals to item
.
The answer from @Taymon using list.index was great.
FWIW, here's a functional approach using the itertools module. It works with any iterable input, not just lists:
>>> from itertools import compress, count, imap, islice
>>> from functools import partial
>>> from operator import eq
>>> def nth_item(n, item, iterable):
indicies = compress(count(), imap(partial(eq, item), iterable))
return next(islice(indicies, n, None), -1)
The example is nice because it shows off how to effectively combine Python's functional toolset. Note, that once the pipeline is set-up, there are no trips around Python's eval loop -- everything gets done at C speed, with a tiny memory footprint, with lazy evaluation, with no variable assignments, and with separately testable components. IOW, it is everything functional programmers dream about :-)
Sample run:
>>> x = [False,True,True,False,True,False,True,False,False,False,True,False,True]
>>> nth_item(50, True, x)
-1
>>> nth_item(0, True, x)
1
>>> nth_item(1, True, x)
2
>>> nth_item(2, True, x)
4
>>> nth_item(3, True, x)
6
I can't say for certain that this is the fastest way, but I imagine it'd be pretty good:
i = -1
for j in xrange(n):
i = x.index(True, i + 1)
The answer is i
.