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
Related
In a python script, I need to assess whether a string contains duplicates of a specific character (e.g., "f") and, if so, remove all but the first instance of that character. Other characters in the string may also have duplicates, but the script should not remove any duplicates other than those of the specified character.
This is what I've got so far. The script runs, but it is not accomplishing the desired task. I modified the reduce() line from the top answer to this question, but it's a little more complex than what I've learned at this point, so it's difficult for me to tell what part of this is wrong.
import re
from functools import reduce
string = "100 ffeet"
dups = ["f", "t"]
for char in dups:
if string.count(char) > 1:
lst = list(string)
reduce(lambda acc, el: acc if re.match(char, el) and el in acc else acc + [el], lst, [])
string = "".join(lst)
Let's create a function that receives a string s and a character c as parameters, and returns a new string where all but the first occurrence of c in s are removed.
We'll be making use of the following functions from Python std lib:
str.find(sub): Return the lowest index in the string where substring sub is found.
str.replace(old, new): Return a copy of the string with all occurrences of substring old replaced by new.
The idea is straightforward:
Find the first index of c in s
If none is found, return s
Make a substring of s starting from the next character after c
Remove all occurrences of c in the substring
Concatenate the first part of s with the updated substring
Return the final string
In Python:
def remove_all_but_first(s, c):
i = s.find(c)
if i == -1:
return s
i += 1
return s[:i] + s[i:].replace(c, '')
Now you can use this function to remove all the characters you want.
def main():
s = '100 ffffffffeet'
dups = ['f', 't', 'x']
print('Before:', s)
for c in dups:
s = remove_all_but_first(s, c)
print('After:', s)
if __name__ == '__main__':
main()
Here is one way that you could do it
string = "100 ffeet"
dups = ["f", "t"]
seen = []
for s in range(len(string)-1,0,-1):
if string[s] in dups and string[s] in seen:
string = string[:s] + '' + string[s+1:]
elif string[s] in dups:
seen.append(string[s])
print(string)
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)
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 = []
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
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.