Replace multiple characters in a string using a set of rules - python

Rules in applyRules is going to be asked to users, but it does not work what it should work which is [“character1:substitution”, “character2:substitution”]
When user put ['b:c','c:d'], it prints c,c. However, it should print d when char is b
The function takes a single character and a set of rules as a list.
And this is what I wrote so far
def applyRules(char, rules):
for rule_list in (rule.split(':') for rule in rules):
char = char.replace(rule_list[0], rule_list[1])
return char
What should I add to make it work appropriately?

If you have multiple rules, this becomes cumbersome. As long as your replacements are done on single characters, you can make this easy with str.translate. That, however, doesn't solve the problem of chained replacements, so you'll have to make use of a while loop that runs until there are no more changes.
def applyRules(string, rules):
mapping = str.maketrans(dict(x.split(':') for x in rules))
while True:
new = string.translate(mapping)
if string == new:
break
string = new
return new
In [1308]: applyRules('bbbbb', ['b:c', 'c:d'])
Out[1308]: 'ddddd'
Single Character Replacement
For replacement involving a single character, the solution simplifies. You could use a dictionary with get:
def applyRules(char, rules):
mapping = dict(x.split(':') for x in rules)
while True:
new = mapping.get(char, char)
if char == new:
break
char = new
return new
This should be much simpler.

You need to apply each and every rule and also, preserve the new string at the same time since strings are immutable in python. You can use translate of str class. Following code works
def applyRules(char, rules):
modified = char
for rule in rules:
r = rule.split(':')
table = str.maketrans({r[0]:r[1]})
modified = modified.translate(table)
return modified
print(applyRules('bbbbb',['b:c','c:d'])) #prints 'ddddd'
print(applyRules('abdecbc',['b:c','c:d'])) #prints 'addeddd'

Related

Transliterate sentence written in 2 different scripts to a single script

I am able to convert an Hindi script written in English back to Hindi
import codecs,string
from indic_transliteration import sanscript
from indic_transliteration.sanscript import SchemeMap, SCHEMES, transliterate
def is_hindi(character):
maxchar = max(character)
if u'\u0900' <= maxchar <= u'\u097f':
return character
else:
print(transliterate(character, sanscript.ITRANS, sanscript.DEVANAGARI)
character = 'bakrya'
is_hindi(character)
Output:
बक्र्य
But If I try to do something like this, I don't get any conversions
character = 'Bakrya विकणे आहे'
is_hindi(character)
Output:
Bakrya विकणे आहे
Expected Output:
बक्र्य विकणे आहे
I also tried the library Polyglot but I am getting similar results with it.
Preface: I know nothing of devanagari, so you will have to bear with me.
First, consider your function. It can return two things, character or None (print just outputs something, it doesn't actually return a value). That makes your first output example originate from the print function, not Python evaluating your last statement.
Then, when you consider your second test string, it will see that there's some Devanagari text and just return the string back. What you have to do, if this transliteration works as I think it does, is to apply this function to every word in your text.
I modified your function to:
def is_hindi(character):
maxchar = max(character)
if u'\u0900' <= maxchar <= u'\u097f':
return character
else:
return transliterate(character, sanscript.ITRANS, sanscript.DEVANAGARI)
and modified your call to
' '.join(map(is_hindi, character.split()))
Let me explain, from right to left. First, I split your test string into the separate words with .split(). Then, I map (i.e., apply the function to every element) the new is_hindi function to this new list. Last, I join the separate words with a space to return your converted string.
Output:
'बक्र्य विकणे आहे'
If I may suggest, I would place this splitting/mapping functionality into another function, to make things easier to apply.
Edit: I had to modify your test string from 'Bakrya विकणे आहे' to 'bakrya विकणे आहे' because B wasn't being converted. This can be fixed in a generic text with character.lower().

Error when trying to build logical parser

So i have these strings stored in database and i want to convert them to python expression to use them with if statement. I will store these strings into list and will loop over them.
For example:
string = "#apple and #banana or #grapes"
i am able to convert this string by replacing # with "a==" and # with "b==" to this :
if a == apple and b == banana or b == grapes
hash refers to a
# refers to b
But when i use eval it throws up error "apple is not defined" because apple is not in quotes. so what i want is this:
if a == "apple" and b == "banana" or b == "grapes"
Is there any way i can do this ?
The strings stored in DB can have any type of format, can have multiple and/or conditions.
Few examples:
string[0] = "#apple and #banana or #grapes"
string[1] = "#apple or #banana and #grapes"
string[2] = "#apple and #banana and #grapes"
There will be else condition where no condition is fullfilled
Thanks
If I understand correctly you are trying so setup something of a logical parser - you want to evaluate if the expression can possibly be true, or not.
#word or #otherword
is always true since it's possible to satisfy this with #=word for example, but
#word and #otherword
is not since it is impossible to satisfy this. The way you were going is using Python's builtin interpreter, but you seem to "make up" variables a and b, which do not exist. Just to give you a starter for such a parser, here is one bad implementation:
from itertools import product
def test(string):
var_dict = {}
word_dict = {}
cur_var = ord('a')
expression = []
for i,w in enumerate(string.split()):
if not i%2:
if w[0] not in var_dict:
var_dict[w[0]] = chr(cur_var)
word_dict[var_dict[w[0]]] = []
cur_var += 1
word_dict[var_dict[w[0]]].append(w[1:])
expression.append('{}=="{}"'.format(var_dict[w[0]],w[1:]))
else: expression.append(w)
expression = ' '.join(expression)
result = {}
for combination in product(
*([(v,w) for w in word_dict[v]] for v in word_dict)):
exec(';'.join('{}="{}"'.format(v,w) for v,w in combination)+';value='+expression,globals(),result)
if result['value']: return True
return False
Beyond not checking if the string is valid, this is not great, but a place to start grasping what you're after.
What this does is create your expression in the first loop, while saving a hash mapping the first characters of words (w[0]) to variables named from a to z (if you want more you need to do better than cur_var+=1). It also maps each such variable to all the words it was assigned to in the original expression (word_dict).
The second loop runs a pretty bad algorithm - product will give all the possible paring of variable and matching word, and I iterate each combination and assign our fake variables the words in an exec command. There are plenty of reasons to avoid exec, but this is easiest for setting the variables. If I found a combination that satisfies the expression, I return True, otherwise False. You cannot use eval if you want to assign stuff (or for if,for,while etc.).
Not this can drastically be improved on by writing your own logical parser to read the string, though it will probably be longer.
#Evaluted as (#apple and #banana) or #grapes) by Python - only #=apple #=banana satisfies this.
>>> test("#apple and #banana or #grapes")
True
#Evaluted as #apple or (#banana and #grapes) by Python - all combinations satisfy this as # does not matter.
>>> test("#apple or #banana and #grapes")
True
#demands both #=banana and #=grapes - impossible.
>>> test("#apple and #banana and #grapes")
False
I am not sure of what you are asking here, but you can use the replace and split functions :
string = "#apple and #banana"
fruits = string.replace("#", "").split("and")
if a == fruits[0] and b == fruits[1]:
Hope this helps

Python substitute elements inside a list

I have the following code that is filtering and printing a list. The final output is json that is in the form of name.example.com. I want to substitute that with name.sub.example.com but I'm having a hard time actually doing that. filterIP is a working bit of code that removes elements entirely and I have been trying to re-use that bit to also modify elements, it doesn't have to be handled this way.
def filterIP(fullList):
regexIP = re.compile(r'\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}$')
return filter(lambda i: not regexIP.search(i), fullList)
def filterSub(fullList2):
regexSub = re.compile(r'example\.com, sub.example.com')
return filter(lambda i: regexSub.search(i), fullList2)
groups = {key : filterSub(filterIP(list(set(items)))) for (key, items) in groups.iteritems() }
print(self.json_format_dict(groups, pretty=True))
This is what I get without filterSub
"type_1": [
"server1.example.com",
"server2.example.com"
],
This is what I get with filterSub
"type_1": [],
This is what I'm trying to get
"type_1": [
"server1.sub.example.com",
"server2.sub.example.com"
],
The statement:
regexSub = re.compile(r'example\.com, sub.example.com')
doesn't do what you think it does. It creates a compiled regular expression that matches the string "example.com" followed by a comma, a space, the string "sub", an arbitrary character, the string "example", an arbitrary character, and the string "com". It does not create any sort of substitution.
Instead, you want to write something like this, using the re.sub function to perform the substitution and using map to apply it:
def filterSub(fullList2):
regexSub = re.compile(r'example\.com')
return map(lambda i: re.sub(regexSub, "sub.example.com", i),
filter(lambda i: re.search(regexSub, i), fullList2))
If the examples are all truly as simple as those you listed, a regex is probably overkill. A simple solution would be to use string .split and .join. This would likely give better performance.
First split the url at the first period:
url = 'server1.example.com'
split_url = url.split('.', 1)
# ['server1', 'example.com']
Then you can use the sub to rejoin the url:
subbed_url = '.sub.'.join(split_url)
# 'server1.sub.example.com'
Of course you can do the split and the join at the same time
'.sub.'.join(url.split('.', 1))
Or create a simple function:
def sub_url(url):
return '.sub.'.join(url.split('.', 1))
To apply this to the list you can take several approaches.
A list comprehension:
subbed_list = [sub_url(url)
for url in url_list]
Map it:
subbed_list = map(sub_url, url_list)
Or my favorite, a generator:
gen_subbed = (sub_url(url)
for url in url_list)
The last looks like a list comprehension but gives the added benefit that you don't rebuild the entire list. It processes the elements one item at a time as the generator is iterated through. If you decide you do need the list later you can simply convert it to a list as follows:
subbed_list = list(gen_subbed)

how can I add mark every two index in String

def Change(_text):
L = len(_text)
_i = 2
_text[_i] = "*"
_i += 2
print(_text)
How can I add a mark e.g:* every two Index In String
Why are you using _ in your variables? If it is for any of these reasons then you are OK, if it is a made up syntax, try not to use it as it might cause unnecessary confusion.
As for your code, try:
def change_text(text):
for i in range(len(text)):
if i % 2 == 0: # check if i = even (not odd)
print(text[:i] + "*" + text[i+1:])
When you run change_text("tryout string") the output will look like:
*ryout string
tr*out string
tryo*t string
tryout*string
tryout s*ring
tryout str*ng
tryout strin*
If you meant something else, name a example input and wished for output.
See How to create a Minimal, Complete, and Verifiable example
PS: Please realize that strings are immutable in Python, so you cannot actually change a string, only create new ones from it.. if you want to actually change it you might be better of saving it as a list for example. Like they have done here.
Are you trying to separate every two letters with an asterix?
testtesttest
te*st*te*st*te*st
You could do this using itertools.zip_longest to split the string up, and '*'.join to rebuild it with the markers inserted
from itertools import zip_longest
def add_marker(s):
return '*'.join([''.join(x) for x in zip_longest(*[iter(s)]*2, fillvalue='')])

Python: Create dynamic loop based on pattern

I'm still learning to code in Python
I want to generate a string based on pattern, the only way I know is by using for loop.
In example code below, I create a loop for "vcvcv" pattern. c=consonant, v=vowel
How to create a dynamic loop, based on pattern that I provide to the script?
eg. if pattern is "cvcvc" the loop should be build to produce the string
Help appeciated.
Thanks.
#!/bin/env python
vowel="aeiou"
consonant="bcdfghjklmnpqrstvwxyz"
lvowel=list(vowel)
lconsonant=list(consonant)
# pattern for "vcvcv" = ababa
for a in lvowel:
for b in lconsonant:
for c in lvowel:
for d in lconsonant:
for e in lvowel:
myname=a+b+c+d+e
print myname
# pattern for "cvcvc" = babab
# how to make the loop dynamic based on pattern ?
Something like this should work:
import itertools
mapping = {
'v': 'aeiou',
'c': 'bcdfghjklmnpqrstvwxyz'
}
pattern = 'vcvcv'
for thing in itertools.product(*map(mapping.get, pattern)):
print ''.join(thing)
Here's roughly how it works:
map(mapping.get, pattern) just converts 'vcv' to ['aeiou', 'bcdfghjklmnpqrstvwxyz', 'aeiou']. It replaces each letter with the corresponding list of characters.
*map(...) unpacks the argument list.
itertools.product() is like a bunch of nested for loops.
''.join(thing) joins the list of characters into a single string.
If you want to do this without itertools, you'll have to make a recursive function.
If you're just getting into programming and want to see a more general solution than the itertools one listed above, then recursion is your best bet, allowing you to arbitrarily nest loops.
There is a slight complication here, which you could use Python generators for, or else use simpler (but messier) constructs. An example of the latter is shown below.
Something like
def continuePattern(pat, strSoFar):
if pat == '':
print strSoFar
elif pat[0] == 'v':
for c in lvowel:
continuePattern(pat[1:], strSoFar + c)
elif pat[0] == 'c':
for c in lconsonant:
continuePattern(pat[1:], strSoFar + c)
This is one of several possible implementations, and one of the two most naive ones I can imagine.
A somewhat more elaborate but easily customizable version for the first n permutations is given below,
def gen_pattern( seq, op = "" ):
vowel="aeiou"
consonant="bcdfghjklmnpqrstvwxyz"
lvowel=list(vowel)
lconsonant=list(consonant)
if ( not seq ):
print op
return
if ( seq[0] == 'v' ):
for v in lvowel:
gen_pattern( seq[1:], op+v )
elif ( seq[0] == 'c' ):
for c in lconsonant:
gen_pattern( seq[1:],op+c )
if __name__ == "__main__":
gen_pattern("vcvcv")
I agree it is more work though!

Categories