def password(passlist):
listt = []
for i in range(0, len(passlist)):
temp = passlist[i]
for j in range(0, len(temp)/2):
if((j+2)%2 == 0) :
t = temp[j]
temp.replace(temp[j], temp[j+2])
temp.replace(temp[j+2], t)
listt.append(temp)
I am passing a list of string
example ["abcd", "bcad"]. for each string i will swap ith character with j character if (i+j)%2 == 0.
My code is going out of the boundary of string.
Please suggest me a better approach to this problem
Here's how I'd do it:
def password(passlist):
def password_single(s):
temp = list(s)
for j in range(0, len(temp) // 2, 2):
temp[j], temp[j+2] = temp[j+2], temp[j]
return ''.join(temp)
return [password_single(s) for s in passlist]
print(password(["abcd", "bcad"]))
Define a function that operates on a single list element (password_single). It's easier to develop and debug that way. In this case, I made it an inner function but it doesn't have to be.
Use three-argument range calls, since it's the same as doing the two-argument + if(index%2 == 0)
Convert strings to lists, perform the swapping and convert back.
Use a "swap" type operation instead of two replaces.
Strings are immutable in python, therefore you cannot swap the characters in place. You have to build a new string.
Moreover, your code does not work for each string in passlist. You iterate through the string in passlist in the first for block, but then you use the temp variable outside that block. This means that the second for loop only iterates on the last string.
Now, a way to do what you want, might be:
for i in range(len(passlist)):
pass_ = passlist[i]
new_pass = [c for c in pass_] # convert the string in a list of chars
for j in range(len(pass_) / 2):
new_pass[j], new_pass[j+2] = new_pass[j+2], new_pass[j] # swap
listt.append(''.join(new_pass)) # convert the list of chars back to string
Related
I'm trying to compress a string in a way that any sequence of letters in strict alphabetical order is swapped with the first letter plus the length of the sequence.
For example, the string "abcdefxylmno", would become: "a6xyl4"
Single letters that aren't in order with the one before or after just stay the way they are.
How do I check that two letters are successors (a,b) and not simply in alphabetical order (a,c)? And how do I keep iterating on the string until I find a letter that doesn't meet this requirement?
I'm also trying to do this in a way that makes it easier to write an inverse function (that given the result string gives me back the original one).
EDIT :
I've managed to get the function working, thanks to your suggestion of using the alphabet string as comparison; now I'm very much stuck on the inverse function: given "a6xyl4" expand it back into "abcdefxylmno".
After quite some time I managed to split the string every time there's a number and I made a function that expands a 2 char string, but it fails to work when I use it on a longer string:
from string import ascii_lowercase as abc
def subString(start,n):
L=[]
ind = abc.index(start)
newAbc = abc[ind:]
for i in range(len(newAbc)):
while i < n:
L.append(newAbc[i])
i+=1
res = ''.join(L)
return res
def unpack(S):
for i in range(len(S)-1):
if S[i] in abc and S[i+1] not in abc:
lett = str(S[i])
num = int(S[i+1])
return subString(lett,num)
def separate(S):
lst = []
for i in S:
lst.append(i)
for el in lst:
if el.isnumeric():
ind = lst.index(el)
lst.insert(ind+1,"-")
a = ''.join(lst)
L = a.split("-")
if S[-1].isnumeric():
L.remove(L[-1])
return L
else:
return L
def inverse(S):
L = separate(S)
for i in L:
return unpack(i)
Each of these functions work singularly, but inverse(S) doesn't output anything. What's the mistake?
You can use the ord() function which returns an integer representing the Unicode character. Sequential letters in alphabetical order differ by 1. Thus said you can implement a simple funtion:
def is_successor(a,b):
# check for marginal cases if we dont ensure
# input restriction somewhere else
if ord(a) not in range(ord('a'), ord('z')) and ord(a) not in range(ord('A'),ord('Z')):
return False
if ord(b) not in range(ord('a'), ord('z')) and ord(b) not in range(ord('A'),ord('Z')):
return False
# returns true if they are sequential
return ((ord(b) - ord(a)) == 1)
You can use chr(int) method for your reversing stage as it returns a string representing a character whose Unicode code point is an integer given as argument.
This builds on the idea that acceptable subsequences will be substrings of the ABC:
from string import ascii_lowercase as abc # 'abcdefg...'
text = 'abcdefxylmno'
stack = []
cache = ''
# collect subsequences
for char in text:
if cache + char in abc:
cache += char
else:
stack.append(cache)
cache = char
# if present, append the last sequence
if cache:
stack.append(cache)
# stack is now ['abcdef', 'xy', 'lmno']
# Build the final string 'a6x2l4'
result = ''.join(f'{s[0]}{len(s)}' if len(s) > 1 else s for s in stack)
I am making a decode method, which selects a set of index values within strings to remove. But right now the problem is i am unable to understand how to select a set of indices to remove
I have tried making a list of items to designate and remove if found in the string, but this would only work for only a few types of string sets.
def decode(string, n):
for i in range(0,len(string), n):
string = string.replace(string[i],'')
return string
here n is the number of values to remove at a given index as well as the index from where to start removing the said values
I understand how to step through an index, but I am not sure how to remove string values according to the index.
print(decode('#P#y#t#h#o#n#',1)) #this works out to be Python
print(decode('AxYLet1x3’s T74codaa7e!',3 )) #this does not, this is supposed to be 'Let's Code'
With "switcher" flag:
def decode(inp_str, n):
s = ''
flag = True
for i in range(0, len(inp_str), n):
flag = not flag
if flag: s += inp_str[i: i + n]
return s
print(decode('#P#y#t#h#o#n#', 1)) # Python
print(decode('AxYLet1x3’s T74codaa7e!', 3)) # Let’s code!
Different approach would be to pick the characters at the right positions:
def decode(string, n):
res = ''
for i in range(len(string)//(2*n)+1):
res += string[2*n*i+n:2*n*i+2*n]
return res
Don't change the size of an iterable when going through it!
The best would be to replace the character with some placeholder that can't be in the string, and then stripping it.
E.g. for your first example you already have that string format. Removing them outside the loop (remember, loop is for marking the characters for deletion) would be:
return ''.join(c for c in string if c!='#')
As for the loop itself in this approach, I'll leave it up to you to debug it now. ;) See how index moves in the loop, see what your replace in fact does! E.g. as I said in the comment, n=1 would go through literally every character, not every second character!
Another solution is smart slicing with indexes. Assuming from your examples that you want to 'remove n, keep n' code:
def decode(string, n):
result = ""
for i in range(n,len(string), 2*n): # first index we want to keep is n, next is 3n, 5n... so we're jumping by 2n each time
result += string[i: i+n]
return result
First, you're returning right after the first iteration. Second, you're only replacing character at n'th position with "".
This should do what you require, it'll replace every 'n' number of characters after every 'n' index:
def decode(string, n):
for i in range(0,len(string)-1,n):
string = string[:i] + string[i+n:] # Remove elements at index "i", till "i+n"
return string
Output:
print(decode('#P#y#t#h#o#n#',1)) # Output = Python
print(decode('AxYLet1x3’s T74codaa7e!',3 )) # Output = Let's Code
The problem at hand is that given a string S, we can transform every letter individually to be lowercase or uppercase to create another string.
Desired result is a list of all possible strings we could create.
Eg:
Input:
S = "a1b2"
Output:
["a1b2", "a1B2", "A1b2", "A1B2"]
I see the below code generates the correct result, but I'm a beginner in Python and can you help me understand how does loop line 5 & 7 work, which assign value to res.
def letterCasePermutation(self, S):
res = ['']
for ch in S:
if ch.isalpha():
res = [i+j for i in res for j in [ch.upper(), ch.lower()]]
else:
res = [i+ch for i in res]
return res
The result is a list of all possible strings up to this point. One call to the function handles the next character.
If the character is a non-letter (line 7), the comprehension simply adds that character to each string in the list.
If the character is a letter, then the new list contains two strings for each one in the input: one with the upper-case version added, one for the lower-case version.
If you're still confused, then I strongly recommend that you make an attempt to understand this with standard debugging techniques. Insert a couple of useful print statements to display the values that confuse you.
def letterCasePermutation(self, S):
res = ['']
for ch in S:
print("char = ", ch)
if ch.isalpha():
res = [i+j for i in res for j in [ch.upper(), ch.lower()]]
else:
res = [i+ch for i in res]
print(res)
return res
letterCasePermutation(None, "a1b2")
Output:
char = a
['A', 'a']
char = 1
['A1', 'a1']
char = b
['A1B', 'A1b', 'a1B', 'a1b']
char = 2
['A1B2', 'A1b2', 'a1B2', 'a1b2']
Best way to analyze this code is include the line:
print(res)
at the end of the outer for loop, as first answer suggests.
Then run it with the string '123' and the string 'abc' which will isolate the two conditionals. This gives the following output:
['1']
['12']
['123']
and
['A','a']
['AB','Ab','aB','ab']
['ABC','ABc','AbC','aBC','Abc','aBc','abC','abc']
Here we can see the loop is just taking the previously generated list as its input, and if the next string char is not a letter, is simply tagging the number/symbol onto the end of each string in the list, via string concatenation. If the next char in the initial input string is a letter, however, then the list is doubled in length by creating two copies for each item in the list, while simultaneously appending an upper version of the new char to the first copy, and a lower version of the new char to the second copy.
For an interesting result, see how the code fails if this change is made at line 2:
res = []
I'm processing some CSS code using Python, since they came from a JSON file (JSON doesn't accept hyphens) they have a particular notation eg.. font-size = fontSize, I'd like to convert every uppercases letter and put them back in the right CSS format inserting a hyphen right before the uppercase.
string = 'borderRadius: 0px, zIndex: 2, transform: translate(170px, 195px) skew(-30deg, 0deg), fontSize:30px'
def getIndices(string):
// get position of each capital letters.
index = [i for i, c in enumerate(string) if c.isupper()]
// code below would be inserted here.
// insert hyphen right before the upperscase
for i in index:
string = string[:i] + '-' + string[i:]
// once done, set string to lowercase since there are no uppercases in CSS properties
string = string.lower()
return string
getIndices(string)
The issue is that each time a hyphen is insered, the position of capital letters becomes off hence the insertion are off too.
I thought about enumarting the index and increasing each int in list by their index number, but I'm probably doing something not quite right.
...
index = [25, 35, 58]
for i, value in enumerate(index):
value = value + (i)
index[i] = value
Any suggestion would be helpful!
If I got it right, this is one way of doing it
for c in my_string:
print c
if c.isupper():
my_string = my_string.replace(c,"-%s" % c.lower())
print my_string # will print fontSize = font-size
You could simply do your insertions in reverse. Otherwise the first ones will change the offset of the last ones. This can be done by calling reversed on the index list before looping over it.
def getIndices(string):
# get position of each capital letters.
index = [i for i, c in enumerate(string) if c.isupper()]
# code below would be inserted here.
# insert hyphen right before the upperscase
for i in reversed(index):
string = string[:i] + '-' + string[i:]
# once done, set string to lowercase since there are no uppercases in CSS properties
string = string.lower()
return string
But it would be better to avoid:
string = string[:i] + '-' + string[i:]
As it makes four new strings every loop iteration.
By building a list and then calling '-'.join. The following works:
def getIndices(string):
# get position of each capital letters.
index = [i for i, c in enumerate(string) if c.isupper()]
# code below would be inserted here.
# insert hyphen right before the upperscase
l = []
e = len(string)
for i in reversed(index):
l.append(string[i:e])
e = i
l.append(string[:e])
l.reverse()
string = '-'.join(l)
# once done, set string to lowercase since there are no uppercases in CSS properties
string = string.lower()
return string
I'd like to do:
l = []
e = len(string)
for i, c in reversed(enumerate(string)):
if c.isupper():
l.append(string[i:e])
e = i
l.append(string[:e])
l.reverse()
string = '-'.join(l)
[The code above does not work as it results in TypeError: argument to reversed() must be a sequence but the error is misleading the argument must either be a sequence or an object that has __reversed__ defined]
But when they added reversed they didn't think about making it applicable to all built-in iterator functions by defining __reversed__ that returns the reverse they are iterating over an iterable that has a __reversed__. I've implemented an enumerate that works that way and find it Pythonic. Oddly it works on xrange but not on enumerate so the code becomes:
def getIndices(string):
l = []
e = len(string)
for i in reversed(xrange(len(string))):
if string[i].isupper():
l.append(string[i:e])
e = i
l.append(string[:e])
l.reverse()
string = '-'.join(l)
string = string.lower()
return string
How to replace the alternative characters in the string with the corresponding index without iterating? For example:
'abcdefghijklmnopqrstuvwxyz'
should be returned as:
'a1c3e5g7i9k11m13o15q17s19u21w23y25'
I have the below code to achieve this. But is there a way to skip the loop or, more pythonic way to achieve this:
string = 'abcdefghijklmnopqrstuvwxyz'
new_string = ''
for i, c in enumerate(string):
if i % 2:
new_string += str(i)
else:
new_string += c
where new_string hold my required value
You could use a list comprehension, and re-join the characters with str.join(); the latter avoids repeated (slow) string concatenation):
newstring = ''.join([str(i) if i % 2 else l for i, l in enumerate(string)])
Note that you can't evade iteration here. Even if you defined a large list of pre-stringified odd numbers here (odds = [str(i) for i in range(1, 1000, 2)]) then re-use that to use slice assignment on a list string_list[1::2] = odds[:len(string) // 2] Python has to iterate under the hood to re-assign the indices. That's the nature of working with an arbitrary-length sequence.