0
L = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
head = 'head'
tail = 'tail'

suppose we can and can only get the iterator of some iterable(L). and we can not know the length of L. Is that possible to print the iterable as:

'head123tail'
'head456tail' 
'head789tail' 
'head10tail'

My try at it is as follows.

L = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
head = 'head'
tail = 'tail'
slice_size = 3

i = iter(L)
try:
    while True:
        counter = 0
        while counter < slice_size:
            e = next(i)
            if counter == 0:
                print(head, end='')
            print(e, end='')
            counter += 1
        else:
            print(tail)
except StopIteration:
    if counter > 0:
        print(tail)
minion
  • 561
  • 4
  • 17
  • You'll have to buffer your 'head', and print it only after next has succeeded, since you can't predict what's next in an iterator without consuming it. – Jacques de Hooge Oct 15 '16 at 18:19
  • @ Jacques, thanks for your suggestion. I modify my preliminary question. – minion Oct 16 '16 at 14:04

2 Answers2

2

Here's one way to do it with itertools.groupby and itertools.count.

groupby on the key function lambda _: next(c)//3 groups the items in the iterables in threes in successions. The logic uses the integer division of the next object in the count item on 3:

from itertools import groupby, count

L = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
head = 'head'
tail = 'tail'

c = count()    
for _, g in groupby(L, lambda _: next(c)//3):
    item = head + ''.join(map(str, g)) + tail
    print(item)

Output:

head123tail
head456tail
head789tail
head10tail
Moses Koledoye
  • 77,341
  • 8
  • 133
  • 139
  • That's a very interesting application of `groupby` and `count`. You might also post this as an answer to [this](http://stackoverflow.com/questions/24527006/split-a-generator-into-chunks-without-pre-walking-it) more general question. – tobias_k Oct 15 '16 at 18:58
  • I don't know how to choose between the current two answers. So I `timeit`. I ran them on my slow computer for 1000 times. Koledoye's answer got 2.9803745844324028 seconds; tobias_k's got 8.567057737782685 seconds. – minion Oct 15 '16 at 19:04
  • @tobias_k Thanks for the pointer. Added an answer: http://stackoverflow.com/a/40063403/3125566 – Moses Koledoye Oct 15 '16 at 19:43
  • @minion You could at least upvote the answers if you're still not sure of which to accept – Moses Koledoye Oct 17 '16 at 06:59
  • @MosesKoledoye, would you slightly change your answer, since my question is 'we can only get the iterator of some iterable(L).' – minion Oct 17 '16 at 07:36
  • @minion you do `it = iter(L)`, and pass the iterator to groupby. But this would be redundant as an iterator can work directly – Moses Koledoye Oct 17 '16 at 07:54
  • @MosesKoledoye, yes, I know. I just think the answer might be better to completely fit the question. Thanks for your wonderful answer. – minion Oct 17 '16 at 07:56
1

You can split the iterator into chunks of three, using chain and slice from itertools and a for loop, and then join them. The for loop will do most of what your try/while True/except construct is doing.

L = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
head = 'head'
tail = 'tail'
slice_size = 3
iterator = iter(L)

from itertools import chain, islice
for first in iterator:
    chunk = chain([first], islice(iterator, slice_size - 1))
    print(head, ''.join(str(x) for x in chunk), tail)

However, if your iterator is just a list, you can just use range with a step parameter:

for start in range(0, len(L), slice_size):
    chunk = L[start : start + slice_size]
    print(head, ''.join(str(x) for x in chunk), tail)
Community
  • 1
  • 1
tobias_k
  • 81,265
  • 12
  • 120
  • 179