Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iterate through a List Multiple times Cyclically starting from the Matching Index

I need to do the following in python. I have a list of strings, list, a string to search for, text, and variable containing the number of elements to print, x. I want to iterate through x no. of consecutive elements of the list, wrapping around to the front of list if necessary.

First I need to find the first element in list that has text as a substring. I will then start with the first element of list after that matched element, and continue to iterate through a total of x consecutive elements of the list, wrapping around if necessary.

How would I do this?

x = 11
text = "string5"
list = ["string1", "string2", "string3", "string4", "string5", "string6", "string7"]

# not sure what to do here...
for elem in list:
    if text in elem:
        #iterate through list, begin with elem and get the next 11 elements
        #once you've reached string7, start over with string1`

In this example I want to end up looking at the following 11 elements:

string6
string7
string1
string2
string3
string4
string5
string6
string7
string1
string2
like image 401
luke Avatar asked Dec 11 '22 02:12

luke


2 Answers

You can use cycle from itertools, maybe in combination with islice and enumerate.

from itertools import cycle, islice

x = 11
text = "string5"
lst = ["string1", "string2", "string3", "string4", "string5", "string6", "string7"]
for i, elem in enumerate(lst):
    if text in elem:
        next11 = list(islice(cycle(lst), i+1, i+1+x))
        print(next11)
        print(len(next11))

Output:

['string6', 'string7', 'string1', 'string2', 'string3', 'string4', 'string5', 'string6', 'string7', 'string1', 'string2']
11
like image 94
tobias_k Avatar answered Jan 19 '23 00:01

tobias_k


For our problem data:

x = 11 #no. of elements to get
text = 'string5' #text to search in elements of list
lst = ['string1', 'string2', 'string3', 'string4', 'string5', 'string6', 'string7'] 
n = len(lst) 

# index of required text
i = lst.index(text)

Algorithm 1: (Cycle & Slice)

Most pythonic way is using the functions, islice and cycle from the itertools module offcourse:

  • Cycle through / Repeat the list infinitely (cycle function)
  • Slice out / Pick the desired elements using start/stop indices from the repeated list (slice function)

Code:

from itertools import cycle, islice 
desired = list( islice( cycle( lst), i+1, i+1+x))

Algorithm 2: (Loop & Modulo Indexing)

A more traditional way would be:

  • Loop through the list x no. of times starting after the matched index, i+1
  • Cycle through the list elements using modulo indexing

Code:

desired = [ lst[ j%n] for j in range(i+1, i+1+x)] 

Algorithm 3 - Bad Implementations: (Rotate & Repeat)

Many poor/ slow implementations are also possible e.g. using numpy functions, like roll, tile:

  • Roll/ Rotate the numpy array so as to start it with the element at the matching index, i
  • Tile / Repeat the array so as to increase its length more than the desired x no. of elements
  • Pick x no. of elements

Code:

def nextx(lst,i,n,x):
    ll = np.array(lst)
    rll = np.roll(ll, n-i)
    trll = np.tile(rll, x%n+1)
    return list(trll[:x])

Output:

>>> nextx(lst,5,7,11)
['string6', 'string7', 'string1', 'string2', 'string3', 'string4', 'string5', 'string6', 'string7', 'string1', 'string2']
>>> nextx(lst,5,7,11) == [lst[j%len(lst)] for j in range(5,5+11)] == list(islice(cycle(lst),5,5+11))
True

Timing:

iPython line magic function %timeit shows that Algorithm 1 is not surprisingly ~1.5 and ~11 times faster than 2 and 3 respectively:

>>> %timeit list( islice( cycle( lst), 5, 5+11))
100000 loops, best of 3: 1.83 µs per loop

>>> %timeit [ lst[ j%len(lst)] for j in range(5, 5+11)] 
100000 loops, best of 3: 2.76 µs per loop

>>> %timeit nextx(lst,5,7,11)
10000 loops, best of 3: 20.6 µs per loop

Conclusion:

Using itertools is the Fastest and Slickest way to go whenever you can use cycle, islice, count, repeat, etc!

like image 39
Pacific Stickler Avatar answered Jan 18 '23 23:01

Pacific Stickler