work.bin
work.bin

Reputation: 1108

How to stop iterating over an iterator at the penultimate item?

While iterating over an iterator I would like to avoid the ultimate item and stop at the penultimate item - how do I do this?

from itertools import product
from collections import namedtuple

param_list = []

Parameter = namedtuple("Parameter", ['bad', 'good'])
param1 = Parameter(["peanut", "gluten"], ["bacon", "pickle"])
param_list.append(param1)
param2 = Parameter([0], [1, 22])
param_list.append(param2)
param3 = Parameter([0, 1], [2, 3, 4, 9])
param_list.append(param3)
param4 = Parameter(["football"], ["running", "yoga"])
param_list.append(param4)

for prod in product(*param_list):  # -- I want to skip the last product --
    for sub_prod in product(*prod):
        prod = [str(x) if type(x) is not str else x for x in sub_prod]
        print ", ".join(prod)

Note -

  1. param_list is a variable length list.
  2. If it were a list instead of an iterator, I would have used for prod in product_list[:-1] :
  3. The print statement is for illustration only.

Upvotes: 3

Views: 472

Answers (3)

Frerich Raabe
Frerich Raabe

Reputation: 94289

Using the pairwise recipe mentioned in the itertools documentation:

from itertools import tee, izip, imap
from operator import itemgetter

def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return izip(a, b)

you can define

def init(iterable):
    return imap(itemgetter(0), pairwise(iterable))

This gets you

>>> list(init(x for x in [1,2,3,4,5]))
[1, 2, 3, 4]

Upvotes: 0

work.bin
work.bin

Reputation: 1108

Based off @khelwood's answer -

from itertools import product
from collections import namedtuple

param_list = []

Parameter = namedtuple("Parameter", ['bad', 'good'])
param1 = Parameter(["peanut", "gluten"], ["bacon", "pickle"])
param_list.append(param1)
param2 = Parameter([0], [1, 22])
param_list.append(param2)
param3 = Parameter([0, 1], [2, 3, 4, 9])
param_list.append(param3)
param4 = Parameter(["football"], ["running", "yoga"])
param_list.append(param4)

# Pulling one item ahead of loop so as to avoid the last item.
iterable = product(*param_list)
prev_prod = next(iterable)

for prod in iterable:
    for sub_prod in product(*prev_prod):  # Using 'prev_prod' instead of 'prod'
        prod_str = [str(x) if type(x) is not str else x for x in sub_prod]
        print ", ".join(prod_str)

    prev_prod = prod

Upvotes: -1

khelwood
khelwood

Reputation: 59095

To avoid using the last item (but not avoid pulling the last item, which is impossible in general), you could do something like this:

def skip_last(seq):
    it = iter(seq)
    p = next(it)
    for n in it:
        yield p
        p = n

>>> print (''.join(skip_last('ABCDE')))
'ABCD'

This is a generator that will iterate through a sequence and yield every item except the last one.

Upvotes: 7

Related Questions