-2

I would like to iterate cyclically over a list (or any other iterable for that matter), and I know you can do this with the cycle function from itertools (as shown here), but this function loops indefinitely. I was wondering if there's a smart way to do this only once (and without using the modulo operator).

What I mean is that I would like to iterate over an iterable in such a way that the last item is the first one. So I would like to start iterating and stop whenever the iterator reaches the beginning of the iterable.

Something like this but less ugly:

points = [1, 2, 3, 4, 5]
start = points[0]
iterator = cycle(points)

p = next(iterator)
while True:
    print(p)
    p = next(iterator)
    if p == start:
        print("quitting at", p)
        break
Community
  • 1
  • 1
aaragon
  • 2,314
  • 4
  • 26
  • 60
  • by once, you mean one copy? – Ayush Nov 25 '15 at 19:59
  • Can you give an example? If `foo` implements what you need, what would be the output of `for x in foo([1,2,3]): print(x)`? – arekolek Nov 25 '15 at 20:52
  • See updated question. – aaragon Nov 25 '15 at 21:05
  • See my updated answer and please explain how what you are trying to achieve is different from a basic for-loop. If it is not different, then this question is a possible duplicate of [Which is the most efficient way to iterate through a list in python?](http://stackoverflow.com/questions/10929724/which-is-the-most-efficient-way-to-iterate-through-a-list-in-python). – arekolek Nov 25 '15 at 23:20
  • If you define `points = [1, 2, 1, 4, 5]`, then your code outputs only the two first elements and "quitting at 1". Is that your desired behavior? (mine would output `1 2 1 4 5` and "quitting at 1", so there's a difference here) – arekolek Nov 25 '15 at 23:56
  • Is it me or what you're asking is "getting data from some iterator from some index to the last one"? I mean, If you want to stop when you're yielding the 'first' item, isn't tat just reaching for the last item and stop? If so, isn't that just `for item in iterator: print(item)` – tglaria Nov 26 '15 at 12:29

3 Answers3

2

Could you explain what you're asking a bit better. What do you mean with cycle and only once?

Isn't that just a for item in list: do someting with item?

EDIT: what I understood about the problem, was that you want to iterate over all the items starting from any index. If so, then:

lista = range(10)
idx = 5
for item in lista[idx:]+lista[:idx]:
    print item

Another Edit: ¿did I get the question right? If what you're asking is stopping at the first item, then you just have iterate on the iterator for the remaining items (and maybe then rebuild the iterator?).

points = [1,3,5,7,9]
iterator = iter(points)

iterator.next()        #Let's remove a couple of items
iterator.next()        #Let's remove a couple of items

for item in iterator:
    print(item)

iterator = iter(points)
tglaria
  • 5,678
  • 2
  • 13
  • 17
  • 2
    This does not provide an answer to the question. To critique or request clarification from an author, leave a comment below their post. - [From Review](/review/low-quality-posts/10338011) – Siyual Nov 25 '15 at 20:34
  • I couldn't make a comment on the post, but now I can. – tglaria Nov 25 '15 at 20:42
1

Sure, but with chain and repeat (for sequences) or tee (for iterators):

>>> from itertools import chain, repeat, tee
>>> print(*chain(*repeat([1, 2, 3], 2)))
1 2 3 1 2 3

>>> print(*chain(*tee(i for i in [1,2,3])))
1 2 3 1 2 3

Asterisk is used to unpack argument lists.

Answer by Ale (chain without repeat) is even better if the iterable is already assigned to a variable and you need actually two cycles, as opposed to say, eight, or any other finite number.

Update

Code equivalent to the one in the updated question:

points = [1, 2, 3, 4, 5]
for x in points:
    print(x)
print("quitting at", points[0])

Which is basically equivalent to the original answer by Tomás Glaría.

Community
  • 1
  • 1
arekolek
  • 9,128
  • 3
  • 58
  • 79
  • Well he said he needed to cycle only once ;-) – Ale Nov 25 '15 at 20:37
  • How is that equivalent? The criterion to break the loop is that you encounter the first node after cyclically iterating through the iterable. – aaragon Nov 25 '15 at 23:26
  • @aaragon It's equivalent in the sense that the output (the side effect) is the same. You can't complain that the mean to achieve your goal is different, since the better mean is what you are asking for. Instead you should refine your question to show that a for-loop is not a sufficient solution (if that's the case). – arekolek Nov 25 '15 at 23:30
  • Nonsense. My question states the problem very clearly. Besides, if I was able to write that code it's obvious I can come up with an answer as trivial as yours. – aaragon Nov 25 '15 at 23:42
  • 1
    @aaragon Your last comment is nonconstructive. Not to mention that it comes off as ungrateful and rude. If you came here for help, let us help you and refine your question so it explains why this "trivial" code is not good enough for you. While it is "very clear" to you, it apparently is not as clear to at least 5 other people. – arekolek Nov 25 '15 at 23:47
0

Why don't you chain or concatenate the iterable?

a = [1,2,3]

for x in a + a:
    print x

from itertools import chain, tee
for x in chain(tee(a, 2)):
    print x

That seems simple enough

Ale
  • 1,315
  • 9
  • 19
  • This will only work if the iterable supports iterating over it more than once. Otherwise, when `chain` goes to iterate over it the second time, the iterable will be consumed. The _fix_ is to use `itertools.tee`. – mgilson Nov 26 '15 at 00:58