2

Currently I'm working on a program. I'd like for it to increment a 5 character alpha numeric value. (Sorry if increment is not the correct word.)

So I'd like for the program to say start at 55aa0 and end at 99zz9. The reason I'd like for it to start at 55aa0 and not 00aa0 is because for what I'm doing it'd be a waste of time.

I'd also like to assign that value to a variable and append it onto it onto the end of another variable and call this one url.

So for example the url could be: domain.de/69xh2

If you need anymore information I'll gladly add it.

count = 0
while count <= n:
    url = ""
    if url.endswith(".jpg"):
        fileType = ".jpg"
    elif url.endswith(".png"):
        fileType = ".png"

    if os.path.isfile("images/" + fileName):
        pass
    else:
        urllib.urlretrieve(url, "images/" + count + fileType)
        count = count + 1
  • Can you say what the correct sequence would look like? Would it be, for instance `['55aa0', '55aa1', '55aa2', '55aa3', '55aa4', '55aa5', '55aa6', '55aa7', '55aa8', '55aa9', '55ab0' ...]`? – Simon Jan 04 '14 at 20:57
  • ['55aa0', '55aa1', ... '55aa9', '55ab0', ... '55az9', '55ba0', ... '55zz9', '56aa0', ... '59zz9', '60aa0'] – Paul Alexander Burkart Jan 04 '14 at 21:26

3 Answers3

2

This sounds like a job for itertools:

from itertools import dropwhile, islice, product

from string import digits, ascii_lowercase as letters

def values():
    """Yield strings in format '00aa0', starting with '55aa0'."""
    def pred(t):
        """Return False once second digit in tuple t reaches '5'."""
        return t[1] < '5'
    for t in dropwhile(pred, product(digits[5:], digits, letters, 
                                     letters, digits)):
        yield "".join(t)

This starts with (using list(islice(values(), 0, 21)) per Simon's suggestion):

['55aa0', '55aa1', '55aa2', '55aa3', '55aa4', '55aa5', 
 '55aa6', '55aa7', '55aa8', '55aa9', '55ab0', '55ab1', 
 '55ab2', '55ab3', '55ab4', '55ab5', '55ab6', '55ab7', 
 '55ab8', '55ab9', '55ac0']

An advantage of using itertools for this is that you don't have to build the whole (304,200-element) list in memory, but can iterate over it:

for s in values():
    # use s

Note that this version is pretty tightly coupled to your requirements (hat tip to Krab for efficiency improvement), but it could easily be modified for more general use.

An even faster version, again from Krab's suggestion:

def values():
    """Yield strings in format '00aa0', starting with '55aa0'."""
    for t in product(map(str, range(55, 100)), letters, letters, digits):
        yield "".join(t)

Note: use xrange and itertools.imap in Python 2.x.

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
  • +1: Elegant solution. And to print out the list, one can use more itertools goodness, e.g. `print (list(itertools.islice(values(), 0, 100)))` ... to print the first hundred items (see [here](http://stackoverflow.com/questions/5234090/how-to-take-the-top-n-items-from-a-generator-or-list-in-python)). – Simon Jan 04 '14 at 21:10
  • 1
    It would be more efficient to filter before enumerating the product, that means instead of the first two usages of `digits` to use something like `digits[5:]` – Krab Jan 04 '14 at 21:10
  • The second use of digits should include 0-4 for when the first digit is e.g. 6, but could be applied to the first – jonrsharpe Jan 04 '14 at 21:11
  • 1
    Oh, yes. I missed it. – Krab Jan 04 '14 at 21:13
  • I still don't like the dropwhile here if you don't mind. :-) `(str(x) for x in range(55,100))` will be the correct replacement for the first two `digits` instances which would eliminate the need for dropwhile. I know it's probably irrelevant in OP's setup, but still according to my measurement, it shaves off 1/4 of the runtime of the function. – Krab Jan 04 '14 at 22:05
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/44527/discussion-between-krab-and-jonrsharpe) – Krab Jan 04 '14 at 22:17
0

It depends on what you want to increment first (ie the number at the end, the first letter, second letter, or the first number), but I would just have separate variables and concatenate them. I would also suggest calling your letters from an array:

    letters = ["a","b"..."y","z"]
    var firstNum = 55
    var firstLetter = letters[0]
    var secondLetter = letters[0]
    var scondNum = 0

then make a loop that increments whatever you want, and concatenate. For example, if you want to increment the last number first:

varList = []
for i in range(0, 100):
  varList.append(firstNum + firstLetter + secondLetter + (secondNum + i))

then you would insert another loop inside that loops that increments the index of the second letter, etc...

hope that helps!

Jona
  • 1,023
  • 2
  • 15
  • 39
0

I used the basic algorithm of adding two integers digit by digit from right to left.

def get_next(st):
    next_str = ""
    increment = '0'*(len(st)-1) + '1'
    index = len(st) -1
    carry = 0
    curr_digit = 0
    while(index>=0):
        if (st[index].isalpha()):

            curr_digit = (ord(st[index]) + int(increment[index]) + carry)
            if curr_digit > ord('z'):
                curr_digit -= ord('a')
                curr_digit %= 26
                curr_digit += ord('a')
                carry = 1
            else:
                carry = 0
            curr_digit = chr(curr_digit)
            next_str += curr_digit

        elif (st[index].isdigit()):
            curr_digit = int(st[index]) + int(increment[index]) + carry
            if curr_digit > 9:
                curr_digit %= 10
                carry = 1
            else:
                carry = 0
            next_str += str(curr_digit)
        index -= 1
    return next_str[::-1]

counter = 20

next_str = '55aa0'
while(counter > 0):
    next_str = get_next(next_str)
    print next_str
    counter -= 1

Output:

55aa1
55aa2
55aa3
55aa4
55aa5
55aa6
55aa7
55aa8
55aa9
55ab0
55ab1
55ab2
55ab3
55ab4
55ab5
55ab6
55ab7
55ab8
55ab9
55ac0
praveen
  • 3,193
  • 2
  • 26
  • 30