How to count sequentially using letters instead of numbers?

Question:

Is there a simple way to count using letters in Python? Meaning, ‘A’ will be used as 1, ‘B’ as 2 and so on, and after ‘Z’ will be ‘AA’, ‘AB’ and so on. So below code would generate:

def get_next_letter(last_letter):
    return last_letter += 1  # pseudo

>>> get_next_letter('a')
'b'
>>> get_next_letter('b')
'c'
>>> get_next_letter('c')
'd'
...
>>> get_next_letter('z')
'aa'
>>> get_next_letter('aa')
'ab'
>>> get_next_letter('ab')
'ac'
...
>>> get_next_letter('az')
'ba'
>>> get_next_letter('ba')
'bb'
...
>>> get_next_letter('zz')
'aaa'
Asked By: hewi

||

Answers:

Based on @Charlie Clark‘s implementation of the openpyxl util get_column_letter, we can have:

def get_number_letter(n):
    letters = []
    while n > 0:
        n, remainder = divmod(n, 26)
        # check for exact division and borrow if needed
        if remainder == 0:
            remainder = 26
            n-= 1
        letters.append(chr(remainder+64))
    return ''.join(reversed(letters))

This gives the letter representation of a number. Now, to increment, we need the reverse. Based on that logic (and the general number base logic), I wrote:

def number_from_string(letters):
    n = 0
    for i, c in enumerate(reversed(letters)):
        n += (ord(c)-64)*26**i
    return n

And now we can combine them to:

def get_next_letter(letters):
    return get_number_letter(number_from_string(letters)+1)

Original answer:

This kind of "counting" is very similar to how Excel indexes its columns. Therefore it is possible to take advantage of the openpyxl package, which has two utility functions: get_column_letter and column_index_from_string:

from openpyxl.utils import get_column_letter, column_index_from_string

def get_next_letter(letters):
    return get_column_letter(column_index_from_string(letters)+1)

NOTE: as this is based on Excel, it is limited to count up-to 'ZZZ'. i.e. calling the function with 'ZZZ' will raise an exception.


Output example for both implementations:

>>> get_next_letter('A')
'B'
>>> get_next_letter('Z')
'AA'
>>> get_next_letter('BD')
'BE'
Answered By: Tomerikoo

Let’s start with the simple special case of getting just the single-character strings.

from string import ascii_lowercase


def population():
    yield from ascii_lowercase

Then

>>> x = population()
>>> list(x)
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
>>> x = population()
>>> next(x)
'a'
>>> next(x)
'b'

So we’d like to add the two-character sequences next:

from string import ascii_lowercase
from itertools import product


def population():
    yield from ascii_lowercase
    yield from map(''.join, product(ascii_lowercase, repeat=2)

Note that the single-character strings are just a special case of the product with repeat=1, so we could have written

from string import ascii_lowercase
from itertools import product


def population():
    yield from map(''.join, product(ascii_lowercase, repeat=1)
    yield from map(''.join, product(ascii_lowercase, repeat=2)

We can write this with a loop:

def population():
    for k in range(1, 3):
        yield from map(''.join, product(ascii_lowercase, repeat=k)

but we don’t necessarily want an artificial upper limit on what strings we can produce; we want, in theory, to produce all of them. For that, we replace range with itertools.count.

from string import ascii_lowercase
from itertools import product, count


def population():
    for k in count(1):
        yield from map(''.join, product(ascii_lowercase, repeat=k)
Answered By: chepner

all proposed are just way too complicated
I came up with below, using a recursive call,
this is it!

def getNextLetter(previous_letter):
    """ 
        'increments' the provide string to the next letter recursively
        raises TypeError if previous_letter is not a string 
        returns "a" if provided previous_letter was emtpy string
    """
    if not isinstance(previous_letter, str):
        raise TypeError("the previous letter should be a letter, doh")
    if previous_letter == '':
        return "a"
    for letter_location in range(len(previous_letter) - 1, -1, -1):
        if previous_letter[letter_location] == "z":
            return getNextLetter(previous_letter[:-1])+"a"
        else:
            characters = "abcdefghijklmnopqrstuvwxyz"
            return (previous_letter[:-1])
                   +characters[characters.find(previous_letter[letter_location])+1]

# EOF
Answered By: hewi
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.