4

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
Pacific Stickler
  • 1,078
  • 8
  • 20
luke
  • 59
  • 1
  • 1
  • 7
  • I meant the element after "text". Sorry – luke Dec 21 '14 at 20:09
  • 1
    You use `if text in elem`. Is it intentional to search if `ŧest` is present in `elem`, or did you mean to compare the strings, `if text == elem`? – spectras Dec 21 '14 at 20:18
  • @spectras OP is now pretty clear that he is looking for a substring: "_first element in list that has text as a substring_" – tobias_k Dec 21 '14 at 20:22
  • Then not only is your answer the one with best style, but it's also the only one that actually does what he wants. – spectras Dec 21 '14 at 20:25
  • No, i don't want to compare the strings. I'm looking for a substring. It's intended. – luke Dec 21 '14 at 20:32

5 Answers5

8

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
tobias_k
  • 81,265
  • 12
  • 120
  • 179
  • Good point using `itertools`. However, I would just use `lst.index(text)` to find out the starting position. – spectras Dec 21 '14 at 20:11
  • 1
    @spectras `index` would not work. He's not testing for equality, but whether `text` is _in_ `elem`. Also, there could be multiple matches. – tobias_k Dec 21 '14 at 20:14
  • Indeed, only given his sample I supposed the use of `in` instead of a comparison was a mistake in his code. Would be worth asking him. And if there are multiple matches, `index` returns the first one. – spectras Dec 21 '14 at 20:17
  • 1
    Upvoting anyway, because this answer is by far the most pythonic one. – spectras Dec 21 '14 at 20:20
4

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!

Nick Brady
  • 6,084
  • 1
  • 46
  • 71
Pacific Stickler
  • 1,078
  • 8
  • 20
1

Hope this will help

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

>>> for i in range(len(list)):
        if text in list[i]:
            a=i+1;
            break;

>>> L = [list[i%len(list)] for i in range(a, a+x)]

>>> L
['string6', 'string7', 'string1', 'string2', 'string3', 'string4', 'string5', 'string6', 'string7',     'string1', 'string2']
Shahriar
  • 13,460
  • 8
  • 78
  • 95
1

Was annoid not getting it, so here is a beautiful solution.. :P

>>> x = 11
>>> text = "string5"
>>> list = ["string1", "string2", "string3", "string4", "string5", "string6", "string7"]
>>> offset = list.index(text)
>>> list_len = len(list)
>>> for i in range(x):
>>>     print(list[(i+offset)%list_len])

It assumes that each string in the list is unique, else you can just set the offset.

Thomas
  • 237
  • 4
  • 15
-1

use a while-loop?

i = 0
n = 0
while 1:
  if i > len(list):
    i = 0
  if (k > x):
    break
  print(list[i])
  i += 1
  k += 1
Thomas
  • 237
  • 4
  • 15