what I'm trying to implement is a function that increments a string by one character, for example:
'AAA' + 1 = 'AAB'
'AAZ' + 1 = 'ABA'
'ZZZ' + 1 = 'AAAA'
I've implemented function for the first two cases, however I can't think of any solution for the third case.
Here's my code :
def new_sku(s):
s = s[::-1]
already_added = False
new_sku = str()
for i in s:
if not already_added:
if (i < 'Z'):
already_added = True
new_sku += chr((ord(i)+1)%65%26 + 65)
else:
new_sku += i
return new_sku[::-1]
Any suggestions ?
If you're dealing with bijective numeration, then you probably have (or should have) functions to convert to/from bijective representation anyway; it'll be a lot easier just to convert to an integer, increment it, then convert back:
def from_bijective(s, digits=string.ascii_uppercase):
return sum(len(digits) ** i * (digits.index(c) + 1)
for i, c in enumerate(reversed(s)))
def to_bijective(n, digits=string.ascii_uppercase):
result = []
while n > 0:
n, mod = divmod(n - 1, len(digits))
result += digits[mod]
return ''.join(reversed(result))
def new_sku(s):
return to_bijective(from_bijective(s) + 1)
How about ?
def new_sku(s):
s = s[::-1]
already_added = False
new_sku = str()
for i in s:
if not already_added:
if (i < 'Z'):
already_added = True
new_sku += chr((ord(i)+1)%65%26 + 65)
else:
new_sku += i
if not already_added: # carry still left?
new_sku += 'A'
return new_sku[::-1]
Sample run :-
$ python sku.py Z
AA
$ python sku.py ZZZ
AAAA
$ python sku.py AAA
AAB
$ python sku.py AAZ
ABA
You have to think of 'AAA', 'ZZZ', ... as representation of the value you manipulate.
First, parse the value:
val = sum(pow(26, i) * (ord(v) - ord('A') + 1) for i, v in enumerate(value[::-1]))
Then, add value to it:
val = val + 1
Edit
The final value is given by:
res = ""
while val > 0:
val, n = divmod(val - 1, 26)
res = chr(n+ord('A')) + res
The lack of representation for zero requires the value passed to divmod to be decremented at each turn, which i have not found a way of doing with a list comprehension.
Edit
Rather than ord() and chr(), it is possible to use string.ascii_uppercase.index() and string.ascii_uppercase[]
You can make use of some recursion here:
def new_sku(s):
s = s[::-1]
new_s = ''
return expand(s.upper(), new_s)[::-1]
import string
chars = string.ascii_uppercase
def expand(s, new_s, carry_forward=True):
if not s:
new_s += 'A' if carry_forward else ''
return new_s
new_s += chars[(ord(s[0]) - ord('A') + carry_forward) % 26]
# Slice the first character, and expand rest of the string
if s[0] == 'Z':
return expand(s[1:], new_s, carry_forward)
else:
return expand(s[1:], new_s, False)
print new_sku('AAB')
print new_sku('AAZ')
print new_sku('ZZZ')
print new_sku('aab')
print new_sku('aaz')
print new_sku('zzz')
Output:
AAC
ABA
AAAA
AAC
ABA
AAAA
I would implement this like a base-26 addition with carry.
So start from the right of the string, add 1. If it reaches Z, wrap to A and bump the next left most character up one. If the left most character reaches Z, add an A to the left of the string.
s = ["Z","Z","Z"]
done = 0
index = len(s) - 1
while done == 0:
if s[index] < "Z":
s[index] = chr(ord(s[index]) + 1)
done = 1
else:
s[index] = "A"
if index == 0:
s = ["A"] + s
done = 1
else:
index = index - 1
print s
Just check if the string is all Zs, and if it is, replace it by a string with length len(s) + 1, consisting of just As:
if s == "Z" * len(s):
return "A" * (len(s) + 1)
alp='ABCDEFGHIJKLMNOPQRSTUVWXYZA'
def rec(s):
if len(s)==0:return 'A'
last_letter=s[-1]
if last_letter=='Z':return rec(s[:-1])+'A'
return s[:-1]+alp[(alp.find(last_letter)+1)]
result
>>> rec('AAA')
'AAB'
>>> rec('AAZ')
'ABA'
>>> rec('ZZZ')
'AAAA'
>>> rec('AZA')
'AZB'
How about this? As a simple way to handle the string getting longer you can prepend a leading '#' and strip it if it wasn't incremented:
>>> def new_sku(s):
def increment(s):
if s.endswith('Z'):
return increment(s[:-1])+'A'
else:
return s[:-1]+chr(ord(s[-1])+1)
t = increment('#'+s)
return t.lstrip('#')
>>> new_sku('AAA')
'AAB'
>>> new_sku('AAZ')
'ABA'
>>> new_sku('ZZZ')
'AAAA'
If the recursion worries you then you can flatten it the way you already did but still use the '#' character added and stripped.
You can use a for-else loop:
from string import ascii_uppercase as au
def solve(strs):
lis = []
for i, c in enumerate(strs[::-1], 1):
ind = au.index(c) + 2
lis.append(au[(ind%26)-1])
if ind <= 26:
break
else:
# This will execute only if the for-loop didn't break.
lis.append('A')
return strs[:-1*i] + "".join(lis[::-1])
print solve('AAA')
print solve('AAZ')
print solve('ZZZ')
print solve('AZZZ')
print solve('ZYZZ')
print solve('ZYYZZ')
output:
AAB
ABA
AAAA
BAAA
ZZAA
ZYZAA
We can see there are 3 conditions totally, you can iterate the string and process one of the conditions.
You can use the string.ascii_uppercase instead of chr and ord
import string
def add(s):
s = list(s)[::-1]
for index, char in enumerate(s):
if char != "Z":
s[index] = string.ascii_uppercase[string.ascii_uppercase.index(char) + 1]
return s[::-1]
elif char == "Z" and (index != len(s) - 1):
s[index] = "A"
elif char == "Z" and (index == len(s) - 1):
s[index] = "A"
return ["A"] + s[::-1]
Related
So far, I have created the one that returns missing string character. Can we also mix it with integers? For example:
Input: "3629aghrjlsbwofhe"
Output: "014578bcdikmnpqtuvxyz"
Current code for alphabet string:
def missingCharacters(Str):
MAX_CHAR = 26
x = [False for i in range(MAX_CHAR)]
for i in range(len(Str)):
if (Str[i] >= 'a' and Str[i] <= 'z'):
x[ord(Str[i]) - ord('a')] = True
result = ""
for i in range(MAX_CHAR):
if (x[i] == False):
result += chr(i + ord('a'))
return result
You can do the following, using some string utils and a conditional generator expression:
from string import digits, ascii_lowercase
def missingCharacters(s):
# if s is long, this will make the repeated membership test O(1)
# s = set(s)
return "".join(c for c in digits + ascii_lowercase if c not in s)
missingCharacters("3629aghrjlsbwofhe")
# '014578cdikmnpqtuvxyz'
I think this function is for some assignment for your college or company. This could be a naive solution for your problem to show our approach in a clear manner. Just a list update on your code for digits.
def missingCharacters(Str):
MAX_CHAR = 26
x = [False for i in range(MAX_CHAR)]
y = []
for i in range(len(Str)):
if (Str[i] >= 'a' and Str[i] <= 'z'):
x[ord(Str[i]) - ord('a')] = True
if (Str[i].isdigit()):
y.append(int(Str[i]))
result = "".join(str(x) for x in range(10) if x not in y)
for i in range(MAX_CHAR):
if (x[i] == False):
result += chr(i + ord('a'))
return result
I have written this function which is supposed to go through a user-provided string like 1-3-5, and output a corresponding series of letters, where A is assigned to 1, B is assigned to 2, C is assigned to 3, etc. So in the case of 1-3-5 the output would be ACE. For 2-3-4, it should print BCD. For ?-3-4 or --3-4 it should still print BCD. Here is the code I have written so far:
def number_to_letter(encoded):
result = ""
start = 0
for char in range(len(encoded)):
if encoded[char] == '-':
i = encoded.index("-")
sub_str = encoded[start:i]
if not sub_str.isdigit():
result += ""
else:
letter = chr(64 + int(sub_str))
if 0 < int(sub_str) < 27:
result += letter
else:
result += ""
start += len(sub_str) + 1
return result
print(num_to_let('4-3-25'))
My output is D, when it should be DCY. I am trying to do this without using any lists or using the split function, just by finding the - character in the sub-string and converting the numbers before it into a letter. What can I do?
You can try doing something like this:
def number_to_letter(encoded):
result = ""
buffer = ""
for ch in encoded:
if ch == '-':
if buffer and 0 < int(buffer) < 27:
result += chr(64 + int(buffer))
buffer = ""
elif ch.isdigit():
buffer += ch
else:
if buffer and 0 < int(buffer) < 27:
result += chr(64 + int(buffer))
return result
print(number_to_letter('1-3-5'))
output:
ACE
Explanation:
we loop for each character and add it to some buffer. when we encounter - (delimiter) we try to parse the buffer and reset it. And we do the same parsing at the end one more time and return the result.
The way the validation works is that, whenever we populate the buffer we check for number validity (using .isdigit()) and when we parse the buffer we check for the range constraints.
import string
alphabet = list(string.ascii_lowercase)
combination = "1-2-3"
def seperate(s, sep='-'):
return [s[:s.index(sep)]] + seperate(s[s.index(sep)+1:]) if sep in s else [s]
combination = seperate(combination)
print("".join([alphabet[int(i)-1] for i in combination]))
the approach of this code is to find the first '-' and then store where it is so next time we can look for the first '-' after the last one
when the comments in my code talk about a cycle means going through the loop (While looping:) once
def number_to_letter(encoded):
letterString = ""
startSubStr = 0
endSubStr = 0
looping = True
while looping:
if endSubStr > (len(encoded)-4):# if we're at the last number we don't look for '-'. we go to the end of the str and end the loop
endSubStr = len(encoded)
looping = False
else:
endSubStr = encoded.index('-', startSubStr) #find the first '-' after the '-' found in the last cycle
number = int(encoded[startSubStr:endSubStr]) #get the number between the '-' found in the last cycle through this loop and the '-' found in this one
if number < 27:
letter = chr(64 + int(number))
letterString += letter
startSubStr = endSubStr + 1 #set the start of the substring to the end so the index function doesn't find the '-' found in this cycle again
return letterString
print(number_to_letter("23-1-1-2")) #>>> WAAB
result:
WAAB
I see you don't want to use split, how about filter? ;)
import itertools
s = '1-2-3'
values = [''.join(e) for e in filter(
lambda l: l != ['-'],
[list(g) for k, g in itertools.groupby(
[*s], lambda s: s.isnumeric()
)
]
)
]
That will essentially do what .split('-') does on s. Also list(s) will behave the same as [*s] if you wanna use that instead.
Now you can just use ord and chr to construct the string you require-
start_pivot = ord('A') - 1
res = ''.join([chr(int(i) + start_pivot) for i in values])
Output
>>> s = '2-3-4'
>>> values = [''.join(e) for e in filter(
...: lambda l: l != ['-'],
...: [list(g) for k, g in itertools.groupby(
...: [*s], lambda s: s.isnumeric()
...: )
...: ]
...: )
...: ]
>>> start_pivot = ord('A') - 1
>>> res = ''.join([chr(int(i) + start_pivot) for i in values])
>>> res
'BCD'
No lists, no dicts. What about RegExp?
import re
def get_letter(n):
if int(n) in range(1,27): return chr(int(n)+64)
def number_to_letter(s):
return re.sub(r'\d+', lambda x: get_letter(x.group()), s).replace('-','')
print(number_to_letter('1-2-26')) # Output: ABZ
No lists, okay. But what about dicts?
def abc(nums):
d = {'-':'','1':'A','2':'B','3':'C','4':'D','5':'E','6':'F','7':'G','8':'H','9':'I','0':'J'}
res = ''
for n in nums: res += d[n]
return res
print(abc('1-2-3-9-0')) # Output: ABCIJ
Here is a corrected version:
def abc(nums):
d = {'-':'','1':'A','2':'B','3':'C','4':'D','5':'E','6':'F','7':'G','8':'H','9':'I','0':'J'}
res = ''
for n in nums:
if n in d:
res += d[n]
return res
print(abc('?-2-3-9-0')) # Output: BCIJ
I am trying to make a string alternate between upper and lower case letters. My current code is this:
def skyline (str1):
result = ''
index = 0
for i in str1:
result += str1[index].upper() + str1[index + 1].lower()
index += 2
return result
When I run the above code I get an error saying String index out of range. How can I fix this?
One way using below with join + enumerate:
s = 'asdfghjkl'
''.join(v.upper() if i%2==0 else v.lower() for i, v in enumerate(s))
#'AsDfGhJkL'
This is the way I would rewrite your logic:
from itertools import islice, zip_longest
def skyline(str1):
result = ''
index = 0
for i, j in zip_longest(str1[::2], islice(str1, 1, None, 2), fillvalue=''):
result += i.upper() + j.lower()
return result
res = skyline('hello')
'HeLlO'
Explanation
Use itertools.zip_longest to iterate chunks of your string.
Use itertools.islice to extract every second character without building a separate string.
Now just iterate through your zipped iterable and append as before.
Try for i in range(len(str1)): and substitute index for i in the code. After, you could do
if i % 2 == 0: result += str1[i].upper()
else: result += str1[i].lower()
For every character in your input string, you are incrementing the index by 2. That's why you are going out of bounds.
Try using length of string for that purpose.
you do not check if your index is still in the size of your string.
It would be necessary to add a condition which verifies if the value of i is always smaller than the string and that i% 2 == 0 and that i == 0 to put the 1st character in Upper
with i% 2 == 0 we will apply the upper one letter on two
for i, __ in enumerate(str1):
if i+1 < len(str1) and i % 2 == 0 or i == 0:
result += str1[i].upper() + str1[i + 1].lower()
I tried to modify as minimal as possible in your code, so that you could understand properly. I just added a for loop with step 2 so that you wouldn't end up with index out of range. And for the final character in case of odd length string, I handled separately.
def skyline (str1):
result = ''
length = len(str1)
for index in range(0, length - 1, 2):
result += str1[index].upper() + str1[index + 1].lower()
if length % 2 == 1:
result += str1[length - 1].upper()
return result
You can use the following code:
def myfunc(str1):
result=''
for i in range(0,len(str1)):
if i % 2 == 0:
result += str1[i].upper()
else:
result += str1[i].lower()
return result
in your code you are get 2 word by one time so you should divide your loop by 2 because your loop work by depending your input string so make an variable like peak and equal it to len(your input input) then peak = int(peak/2) it will solve your pr
def func(name):
counter1 = 0
counter2 = 1
string = ''
peak = len(name)
peak = int(peak/2)
for letter in range(1,peak+1):
string += name[counter1].lower() + name[counter2].upper()
counter1 +=2
counter2 +=2
return string
I am new to python.
I want to shift char by its position. for e.g.
pos= 3 then replace c by z , b by y , a by x, Same like C by Z , B by Y and A by X
What I have tried ?
I have tried with hardcoded values as,
for i in s:
if ord(i) == 99:
return z
if ord(i) == 98:
return x
...
...
Is there is any builtin function is available to do this ? or Any other simpler way to achieve this ?
Edit:
if string would be "abcdef"
the output would be "xyzabc"
I user input "AbCdEf" then output would be "XyZaBc"
You can use ord combined with chr:
ord("e") + 3
-> 104
chr(ord("e") + 3)
-> 'h'
From your requirements, I understand that you need to demonstrate a Substitution Cipher. It is very simple to implement. I will give you function outline pseudocode:
char substitutionCipher(int leftRotateBy, char alphabet) {
int pos = (int) alphabet - (int) 'a';
pos -= leftRotateBy;
if(pos < 0) {
pos += 26;
}
return (char) (pos + (int) 'a');
}
Please note that this outline will work only for lowercase letters. You can modify it to work for uppercase letters also. Thanks!
If you're using Python 2 and you are wanting to convert entire sentences, consider making a character translation table. https://docs.python.org/2/library/string.html#string.maketrans
import string
def caesar_shift(secret, shift):
lcase = string.ascii_lowercase
ucase = string.ascii_uppercase
shift = shift % len(lcase) # force shift to be between 0 and 25, inclusive
trans = string.maketrans(
''.join([lcase,ucase]),
''.join([lcase[shift:], lcase[shift:],
ucase[shift:], ucase[:shift]]))
return string.translate(secret, trans)
Keep in mind that string.maketrans and string.translate were removed in deprecated in Python 3 as part of improving Python's Unicode string handling.
Use negative or positive values to shift left/right.
https://en.wikipedia.org/wiki/Caesar_cipher
import string
def convert(c, pos):
charset = string.lowercase
index = charset.find(c)
if index >= 0:
return charset[(index - pos) % len(charset)]
charset = string.uppercase
index = charset.find(c)
if index >= 0:
return charset[(index - pos) % len(charset)]
return c
assert convert("c", 3) == "z"
assert convert("b", 3) == "y"
assert convert("a", 3) == "x"
assert convert("C", 3) == "Z"
assert convert("B", 3) == "Y"
assert convert("A", 3) == "X"
Update for those who are interested in better performance:
import string
def get_converter(pos):
def rot(s, pos):
offset = -pos % len(s)
return s[offset:] + s[:offset]
def getzip(charset, pos):
return zip(charset, rot(charset, pos))
def getdict(pos):
return dict(getzip(string.ascii_lowercase, pos) + getzip(string.ascii_uppercase, pos))
chardict = getdict(pos)
def converter(c):
return chardict.get(c, c)
return converter
convert = get_converter(3)
assert convert("c") == "z"
assert convert("b") == "y"
assert convert("a") == "x"
assert convert("C") == "Z"
assert convert("B") == "Y"
assert convert("A") == "X"
assert convert("!") == "!"
I'm trying to create a loop to generate and print strings as follows:
Alphanumeric characters only:
0-9 are before A-Z, which are before a-z,
Length goes up to 4 characters.
So, it would print:
all strings from 0-z
then from 00-zz
then from 000-zzz
then from 0000-zzzz
then it stops.
from string import digits, ascii_uppercase, ascii_lowercase
from itertools import product
chars = digits + ascii_uppercase + ascii_lowercase
for n in range(1, 4 + 1):
for comb in product(chars, repeat=n):
print ''.join(comb)
This first makes a string of all the numbers, uppercase letters, and lowercase letters.
Then, for each length from 1-4, it prints every possible combination of those numbers and letters.
Keep in mind this is A LOT of combinations -- 62^4 + 62^3 + 62^2 + 62.
I dislike the answer given before me using product since looking at its implementation in the python documentation it seem to span the entire thing into a list in memory before starting to yield the results.
This is very bad for your case since, as agf himself said, the number of permutation here is huge (well over a million). For this case the yield statement was created - so that huge lists could be dynamically generated rather than spanned in memory (I also disliked the wasteful range where xrange is perfectly applicable).
I'd go for a solution like this:
def generate(chars, length, prefix = None):
if length < 1:
return
if not prefix:
prefix = ''
for char in chars:
permutation = prefix + char
if length == 1:
yield permutation
else:
for sub_permutation in generate(chars, length - 1, prefix = permutation):
yield sub_permutation
This way, all that spans in memory is a recursive stack "n" deep, where "n" is the length of your permutations (4 in this case) and only a single element is returned each time.
chars is the set of chars to choose from, length is 4 and the use is rather similar to products, except that it doesn't span the whole list in memory during run time.
I coded this today. It does exactly what you want and more. It's extendable as well
def lastCase (lst):
for i in range(0, len(lst)):
if ( lst[i] != '_' ):
return False
return True
l = [''] * 4 #change size here if needed. I used 4
l[0] = '0'
index = 0
while ( not lastCase(l) ):
if ( ord(l[index]) > ord('_') ):
l[index] = '0'
index += 1
while( l[index] == '_' ):
l[index] = '0'
index += 1
if (l[index] == ''):
l[index] = '0'
#print or process generated string
print(''.join(l))
l[index] = chr(ord(l[index]) +1)
if ( ord(l[index]) > ord('9') and ord(l[index]) < ord('A') ):
l[index] = 'A'
elif ( ord(l[index]) > ord('Z') and ord(l[index]) < ord('_') ):
l[index] = '_'
index = 0
print (''.join(l))
from string import digits, ascii_uppercase, ascii_lowercase
from itertools import product
chars = digits + ascii_uppercase + ascii_lowercase
def give_me_next(lst):
lst = lst[::-1]
change_next = False
change = True
n = 0
for x in lst:
if change_next == True:
change_next = False
pos = chars.find(x)
try:
a = chars[pos+1]
lst = list(lst)
lst[n] = a
lst = "".join(lst)
x = a
except:
lst = list(lst)
lst[n] = '0'
lst = "".join(lst)
change_next = True
x = '0'
pos = chars.find(x)
try:
a = chars[pos+1]
if change == True:
lst = list(lst)
lst[n] = a
lst = "".join(lst)
change = False
except:
lst = list(lst)
lst[n] = '0'
lst = "".join(lst)
change_next = True
n = n + 1
lst = lst[::-1]
return lst
a= give_me_next('zzzzz')
while True:
a = give_me_next(a)
print a
This seems like the simplest solution to me:
from string import digits, ascii_uppercase, ascii_lowercase
chars = digits + ascii_uppercase + ascii_lowercase
all_str = [''.join([a]) for a in chars] \
+ [''.join([a,b]) for a in chars for b in chars] \
+ [''.join([a,b,c]) for a in chars for b in chars for c in chars] \
+ [''.join([a,b,c,d]) for a in chars for b in chars for c in chars for d in chars]
print(all_str)
print("Number of strings:", len(all_str))
Example for strings with maximum 2 characters.
Of course, there may be a way to generalize to any max number of characters per string, but since you have a specific need for strings up to 4 characters, it's fine.