Search code examples
pythongeneratorpermutationpython-itertools

Python 2.7: iterate over all combinations of a string in one line of code


I am trying to iterate through all possible combinations of a string between lengths 1 to 8 (inclusive). My main goal is to have the code in as few lines as possible. To do this, I'm currently using Python's itertools library:

import itertools, string
for i in xrange(1, 9):
    for x in itertools.imap(''.join, itertools.product(string.letters + string.digits + string.punctuation, repeat = i)):
        if x == some_string:
            # do something, like print x
            print x  
            break
    else:
        continue
    break

I want to be able to do the iterating in one line, so I can break out of the inner and outer for loops at once, and won't need the else:, continue, break etc. Something like this: (using nested for loops)

for x in (itertools.imap(''.join, itertools.product(string.letters + string.digits + string.punctuation, repeat = i)) for i in xrange(1, 9)):
    if x == some_string:
        print x
        break

However, x turns out to be an <itertools.imap object>. So I tried to iterate over x, using more nested for loops (because if I used inner for loops, I will have to break multiple times again):

for y in (x for x in (itertools.imap(''.join, itertools.product(string.letters + string.digits + string.punctuation, repeat = i)) for i in xrange(1, 9))):
    if y == some_string:
        print y
        break

Unfortunately, that still doesn't work; y is somehow still an <itertools.imap object>. I am a self-learned Python programmer, and generators, iterables etc. are confusing me sometimes. Can somebody please help me get the iterating down to one line? Thank you so much.


Solution

  • You are pretty close, you need itertools.chain to "contatenate" several iterators like:

    alphabet = "ab"
    
    for x in itertools.imap(''.join, itertools.chain(
              *(itertools.product(alphabet, repeat=i)
                for i in range(1,9)))):
        print x
    

    Note that chain takes a list of arguments, so I'm using varargs with a generator expression.

    EDIT: Using the itertools.chain.from_iterable from comments:

    from itertools import chain, imap, product
    
    alphabet = "ab"
    
    for x in imap(''.join, chain.from_iterable(product(alphabet, repeat=i)
                                 for i in range(1,9))):
        print x