Created
July 14, 2020 16:07
-
-
Save maximusfox/314dc84349d73cbf14fd66e0baecedde to your computer and use it in GitHub Desktop.
Python string incrementation like in Ruby
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# -*- coding: utf-8 -*- | |
import string | |
def succ(s): | |
if not isinstance(s, (str, string)): | |
raise TypeError("succ works only with strings") | |
if not s: return | |
if max(map(ord, s)) > 127: | |
raise TypeError("succ currently only supports ascii") | |
# Three different character category honoured | |
# 1. ascii lowercase alpha | |
# 2. ascii uppercase alpha | |
# 3. digits | |
# 4. ascii nonalpha characters (the entire ascii set) | |
lower = string.ascii_lowercase + 'a' | |
upper = string.ascii_uppercase + 'A' | |
digits = string.digits + '0' | |
nonalpha = list(map(chr, range(0, 256))) + [chr(0)] | |
def incr(ch): | |
''' | |
Generates the next character in sequence using the following rules | |
1. Incrementing a digit always results in another digit | |
2. Incrementing a letter results in another letter of the same case. | |
3. Incrementing nonalphanumerics uses the underlying | |
character set’s collating sequence. | |
''' | |
if ch.isdigit(): return digits[ord(ch) - ord("0") + 1] | |
if ch.islower(): return lower[ord(ch) - ord('a') + 1] | |
if ch.isupper(): return upper[ord(ch) - ord('A') + 1] | |
return nonalpha[ord(ch) + 1] | |
def last(ch): | |
''' | |
Returns the last character in its catagory | |
''' | |
if ch.isdigit(): return digits[-2] | |
if ch.islower(): return lower[-2] | |
if ch.isupper(): return upper[-2] | |
return nonalpha[-2] | |
def first(ch): | |
''' | |
Returns the last first in its catagory | |
''' | |
if ch.isdigit(): return digits[0] | |
if ch.islower(): return lower[0] | |
if ch.isupper(): return upper[0] | |
return nonalpha[0] | |
def islast(ch): | |
''' | |
Checks if next increment would generate a carry | |
''' | |
return ch == last(ch) | |
s = list(s)[::-1] | |
carry = True | |
try: | |
index = next(i for i, e in enumerate(s) if e.isalnum()) | |
except StopIteration: | |
index = 0 | |
while carry: | |
if index == len(s): # Add a character for Overflow | |
s.append(first(s[index - 1])) | |
break | |
carry = True if islast(s[index]) else False | |
s[index] = incr(s[index]) | |
index += 1 | |
return ''.join(s[::-1]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment