reversing a string, if it contains only mirrorable letters - python

here is the question. No this is not homework this is self taught.
I am attempting the following question:
The mirror image of string vow is string wov, and the mirror image wood is string boow. The mirror image of string bed cannot be represented as a string, however, because the mirror image of e is not a valid character.
The characters in the alphabet whose mirror image is a valid character are: b, d, i, o, v, w, and x. Develop function mirror() that takes a string and returns its mirror image but only if the mirror image can be represented using letters in the alphabet.
**My Code **
def mirror(word):
'''returns mirror image of word but if it can be represented
using letters in the alphabet'''
for i in range (0,len(word)):
if i == 'bdiovwx':
print(str(word.reverse))
else:
print ('NOPE')
the result i am getting is nothing. As in i execute the program and nothing prints.
Any thoughts?
Thank you

You don't need a for loop for this. Essentially, you are testing if all the characters of the word belong to one of the characters in 'bdiovwx', and so, you can just check fr exactly that - the subset relationship amongst the set of characters in word, and 'bdiovwx'.
Also, strings do not have a reverse method in python, so you can use the trick "string"[::-1] to print its reverse.
def mirror(word):
'''
returns mirror image of word but if it can be represented
using letters in the alphabet
'''
if set(word).issubset(set('bdiovwx')):
print(word[::-1])
else:
print ('NOPE')

By fixing your algorithm, you can get the results. You just need to loop through the items, add all the elements to set and compare it later if it's included in the set of alphabets u defined 'bdiovwx' , notFound is used to stop the iteration when you found variable that doesn't belong to the accepted set u defined 'bdiovwx'
word[::-1] produces the reversed word
def mirror(word):
'''returns mirror image of word but if it can be represented
using letters in the alphabet'''
i,notFound=0,True
D=set()
while notFound and i < len(list(word)):
if word[i] not in list('bdiovwx'):
notFound= False
D.add(word[i])
i+=1
if notFound and D.issubset(set(list('bdiovwx'))):
print word[::-1]
else:
print ('NOPE')
You can also use ALL operator, to verify that all the characthers in word are in 'bdiovwx', if it's True, then print the inverse, else; you print None
def mirror(word):
if all(x for x in list(word) if x in list('bdiovwx')):
print word[::-1]
else:
print ('NOPE')

Note that 'd' reflects a 'b' and 'b' reflects a 'd' in the mirror :)
def mirror(word):
letters = list("bdiovwx")
reflect_letters = list("dbiovwx")
if set(word).issubset(letters):
print ''.join([reflect_letters[letters.index(x)] for x in word[::-1]])
else:
print "NOPE!"

Related

How can I change numbers to a letter in a string?

I'm trying to change numbers in a string to an assigned letter (0 would be 'A', 1 => 'B' and so on...).
In this case I must use a function (def) to change the numbers to the assigned letter. I tried doing the program with if's in a for cycle with indices and it worked, but apperantly there is an easier and shorter solution. I tried doing it with isdigit() instead, but my function doesn't recognize the numbers in the given word and just prints out the same word
You can use a dictionary to map digits to letters, write a generator to get those letters and join it all into a resultant string. The dictionary's get method either gets the value or returns a default. Use this to return any non-digit characters.
>>> digit_map = dict(zip('123456789', 'ABCDEFGHI'))
>>> test = 'a1b2c3'
>>> "".join(digit_map.get(c,c) for c in test)
'aAbBcC'
Use ord to get the numeric value of a character, and chr to turn the numeric value back into a character. That lets you convert a number like 1 into its offset from another character.
Then use join to put it all together:
>>> ''.join(chr(int(c)+ord('A')) if c.isdecimal() else c for c in 'a1b2c3')
'aBbCcD'
One solution would be creating an array that would store each letter as a string as such:
alphabet = ["A", "B", "C"] // and so on
Then you could loop trough the string and find all numbers, then for each numbers get the corresponding letter by accessing it from the array as such py alphabet[i] and feed it back to the string and return that
Here is a simple function that you can use to convert numbers to letters in a given string:
def convert_numbers_to_letters(s):
# Create a dictionary that maps numbers to letters
number_to_letter = {str(i): chr(i + ord('A')) for i in range(10)}
# Iterate through each character in the string
result = ""
for c in s:
# If the character is a number, convert it to a letter
if c.isdigit():
result += number_to_letter[c]
# Otherwise, just add the character to the result
else:
result += c
return result

Why does my code remove 999 in my replacement code?

I have the code below to replace all punctuation with 999 and all alphabet characters with its number position. I have included the print statement that confirms punctuation is being replaced. However I seem to override with my remaining code to replace the other characters.
import string
def encode(text):
punct = '''!()-[]{};:'"\,<>./?##$%^&*_~'''
for x in text.lower():
if x in punct:
text = text.replace(x, ".999")
print(text)
nums = [str(ord(x) - 96)
for x in text.lower()
if x >= 'a' and x <= 'z'
]
return ".".join(nums)
print(encode(str(input("Enter Text: "))))
Input: 'Morning! \n'
Output: '13.15.18.14.9.14.7 \n'
Expected Output: 13.15.18.14.9.14.7.999
No, you have two independent logical "stories" here. One replaces punctuation with 999. The other filters out all the letters and builds an independent list of their alphabetic positions.
nums = [str(ord(x) - 96)
for x in text.lower()
if x >= 'a' and x <= 'z'
]
return ".".join(nums)
Note that this does nothing to alter text, and it takes nothing but letters from text. If you want to include the numbers, do so:
nums = [str(ord(x) - 96)
if x >= 'a' and x <= 'z'
else x
for x in text.lower()
]
return ".".join(nums)
Output of print(encode("[hello]")):
..9.9.9.8.5.12.12.15...9.9.9
nums = [str(ord(x) - 96)
for x in text.lower()
if x >= 'a' and x <= 'z'
]
This means: take every character from the lowercase version of the string, and only if it is between 'a' and 'z', convert the value and put the result in nums.
In the first step, you replace a bunch of punctuation with text that includes '.' and '9' characters. But neither '9' nor '.' is between 'a' and 'z', so of course neither is preserved in the second step.
Now that I understand what you are going for: you have fundamentally the wrong approach to splitting up the problem. You want to separate the two halves of the rule for "encoding" a given part of the input. But what you want to do is separate the whole rule for encoding a single element, from the process of applying a single-element rule to the whole input. After all - that is what list comprehensions do.
This is the concept of separation of concerns. The two business rules are part of the same concern - because implementing one rule doesn't help you implement the other. Being able to encode one input character, though, does help you encode the whole string, because there is a tool for that exact job.
We can have a complicated rule for single characters - no problem. Just put it in a separate function, so that we can give it a meaningful name and keep things simple to understand. Conceptually, our individual-character encoding is a numeric value, so we will consistently encode as a number, and then let the string-encoding process do the conversion.
def encode_char(c):
if c in '''!()-[]{};:'"\,<>./?##$%^&*_~''':
return 999
if 'a' <= c.lower() <= 'z':
return ord(c) - 96
# You should think about what to do in other cases!
# In particular, you don't want digit symbols 1 through 9 to be
# confused with letters A through I.
# So I leave the rest up to you, depending on your requirements.
Now we can apply the overall encoding process: we want a string that puts '.' in between the string representations of the values. That's straightforward:
def encode(text):
return '.'.join(str(encode_char(c)) for c in text)

How to index list when there is 2 of the same char

I have been trying to make the even letters in a string become upper-cased and the odd letters to become lower-cased with a function, like so:
def myfunc('apple'):
#OUTPUTS: 'ApPlE'
This is what I made:
def myfunc(mystring):
stringList = [letter for letter in mystring]
for letter in stringList[1::2]:
stringList[stringList.index(letter)] = letter.lower()
for letter in stringList[::2]:
stringList[stringList.index(letter)] = letter.upper()
return ''.join(stringList)
I believe that, when I use words like 'apple' where there is two identical letters, the index() function can only manage to give me the index of the first 'p', if my word is apple.
It returns:
'APplE'
How could I fix this?
By iterating over the indices of the string, using the built-in function enumerate, together with the characters of the string (strings are also iterable):
def myfunc(mystring):
out = []
for i, c in enumerate(mystring):
if i % 2 == 0:
out.append(c.upper())
else:
out.append(c.lower())
return "".join(out)
Example output:
>>> myfunc('apple')
'ApPlE'
This is also a lot more efficient, since it only iterates over the string once. Your code iterates many times (each stringList.index call does a linear search for the letter).
If you want to make it a bit harder to read but re-use a bit more of what you already have, you can also use this, but I would not recommend it (as it iterates three times over the string, once to build the list and then twice to replace the characters):
def myfunc(mystring):
stringList = list(mystring)
stringList[::2] = map(str.upper, stringList[::2])
stringList[1::2] = map(str.lower, stringList[1::2])
return "".join(stringList)
The method list.index returns the index of the first occurence, making it unfit for recovering the index of the current element. Instead, you should use enumerate, this will allow you to get the expected result with a single list-comprehension.
def myFunc(s):
return ''.join([c.lower() if i % 2 else c.upper() for i, c in enumerate(s)])
print(myFunc('apple')) # ApPlE

Changing a string into it's number value in python. [ord()]

I am writing a program in python that calculates the number equivalent of a string and prints it. (By number equivalent, I mean a=1 b=2 ... so the seventh letter in the alphabet is converted to the number 7.) So if the word was abc then it would turn out to be 123. And if possible, the numbers (in my example 123) are added. (So in my example the result that is printing would be 6.) I tried doing this letter by letter, using the ord() function. But that ended up being where you type each letter and press enter and then add spaces for empty letters. The code was meant to calculate the number value of each letter in the word, add all of the values, and tell True if the number was 100 or false if it was anything else. Here it is... `
#collect info
ar=raw_input('Letter')
br=raw_input('Letter')
cr=raw_input('Letter')
dr=raw_input('Letter')
er=raw_input('Letter')
fr=raw_input('Letter')
gr=raw_input('Letter')
hr=raw_input('Letter')
ir=raw_input('Letter')
jr=raw_input('Letter')
kr=raw_input('Letter')
lr=raw_input('Letter')
mr=raw_input('Letter')
nr=raw_input('Letter')
#ord it
ap=ord(ar)
bp=ord(br)
cp=ord(cr)
dp=ord(dr)
ep=ord(er)
fp=ord(fr)
gp=ord(gr)
hp=ord(hr)
ip=ord(ir)
jp=ord(jr)
kp=ord(kr)
lp=ord(lr)
mp=ord(mr)
np=ord(nr)
#sub 96
a=(ap-96)
b=(bp-96)
c=(cp-96)
d=(dp-96)
e=(ep-96)
f=(fp-96)
g=(gp-96)
h=(hp-96)
i=(ip-96)
j=(jp-96)
k=(kp-96)
l=(lp-96)
m=(mp-96)
n=(np-96)
#chk for 96
if a==-64:
a=0
if b==-64:
b=0
if c==-64:
c=0
if d==-64:
d=0
if e==-64:
e=0
if f==-64:
f=0
if g==-64:
g=0
if h==-64:
h=0
if i==-64:
i=0
if j==-64:
j=0
if k==-64:
k=0
if l==-64:
l=0
if m==-64:
m=0
if n==-64:
n=0
#add
value=a+b+c+d+e+f+g+h+i+j+k+l+m+n
#spit
if value==100:
print 'True (100)'
if value<100 or value>100:
print 'False (', value, ')'`
I can't figure out how to do this. So, an explanation would be nice, a full code re-write would be enjoyed, but not required.
-Adam
P.S. If there is anything I forgot to add to this question, just tell me.
Assuming everything is lowercase and input is only a-z
sum((ord(c) - ord('a') + 1 for c in s))
>>> from string import lowercase,uppercase
>>> alphabet = lowercase+uppercase
>>> mapper = {c:i for i,c in enumerate(alphabet,start=1)}
>>> aword = "Letter"
>>> sum(mapper[l] for l in aword)
106
Create a dictionary which maps a character c to the position it is in the alphabet i. We then pass sum a generator expression which looks up i for each character in aword, resulting in all the character values being summed.
>>> aword="iabcdefghijklm"
>>> value=sum(map(ord,aword),(1-ord("a"))*len(aword))
>>> print value==100, value
True 100
>>>

Find symmetric words in a text [duplicate]

This question already has answers here:
how to find words that made up of letter exactly facing each other? (python) [closed]
(4 answers)
Closed 9 years ago.
I have to write a function which takes one arguments text containing a block of text in the form of a str, and returns a sorted list of “symmetric” words. A symmetric word is defined as a word where for all values i, the letter i positions from the start of the word and the letter i positions from the end of the word are equi-distant from the respective ends of the alphabet. For example, bevy is a symmetric word as: b (1 position from the start of the word) is the second letter of the alphabet and y (1 position from the end of the word) is the second-last letter of the alphabet; and e (2 positions from the start of the word) is the fifth letter of the alphabet and v (2 positions from the end of the word) is the fifth-last letter of the alphabet.
For example:
>>> symmetrics("boy bread aloz bray")
['aloz','boy']
>>> symmetrics("There is a car and a book;")
['a']
All I can think about the solution is this but I can't run it since it's wrong:
def symmetrics(text):
func_char= ",.?!:'\/"
for letter in text:
if letter in func_char:
text = text.replace(letter, ' ')
alpha1 = 'abcdefghijklmnopqrstuvwxyz'
alpha2 = 'zyxwvutsrqponmlkjihgfedcba'
sym = []
for word in text.lower().split():
n = range(0,len(word))
if word[n] == word[len(word)-1-n]:
sym.append(word)
return sym
The code above doesn't take into account the position of alpha1 and alpha2 as I don't know how to put it. Is there anyone can help me?
Here is a hint:
In [16]: alpha1.index('b')
Out[16]: 1
In [17]: alpha2.index('y')
Out[17]: 1
An alternative way to approach the problem is by using the str.translate() method:
import string
def is_sym(word):
alpha1 = 'abcdefghijklmnopqrstuvwxyz'
alpha2 = 'zyxwvutsrqponmlkjihgfedcba'
tr = string.maketrans(alpha1, alpha2)
n = len(word) // 2
return word[:n] == word[::-1][:n].translate(tr)
print(is_sym('aloz'))
print(is_sym('boy'))
print(is_sym('bread'))
(The building of the translation table can be easily factored out.)
The for loop could be modified as:
for word in text.lower().split():
for n in range(0,len(word)//2):
if alpha1.index(word[n]) != alpha2.index(word[len(word)-1-n]):
break
else:
sym.append(word)
return sym
According to your symmetric rule, we may verify a symmetric word with the following is_symmetric_word function:
def is_symmetric_word(word):
alpha1 = 'abcdefghijklmnopqrstuvwxyz'
alpha2 = 'zyxwvutsrqponmlkjihgfedcba'
length = len(word)
for i in range(length / 2):
if alpha1.index(word[i]) != alpha2.index(word[length - 1 - i]):
return False
return True
And then the whole function to get all unique symmetric words out of a text can be defined as:
def is_symmetrics(text):
func_char= ",.?!:'\/;"
for letter in text:
if letter in func_char:
text = text.replace(letter, ' ')
sym = []
for word in text.lower().split():
if is_symmetric_word(word) and not (word in sym):
sym.append(word)
return sym
The following are two test cases from you:
is_symmetrics("boy bread aloz bray") #['boy', 'aloz']
is_symmetrics("There is a car and a book;") #['a']
Code first. Discussion below the code.
import string
# get alphabet and reversed alphabet
try:
# Python 2.x
alpha1 = string.lowercase
except AttributeError:
# Python 3.x and newer
alpha1 = string.ascii_lowercase
alpha2 = alpha1[::-1] # use slicing to reverse alpha1
# make a dictionary where the key, value pairs are symmetric
# for example symd['a'] == 'z', symd['b'] == 'y', and so on
_symd = dict(zip(alpha1, alpha2))
def is_symmetric_word(word):
if not word:
return False # zero-length word is not symmetric
i1 = 0
i2 = len(word) - 1
while True:
if i1 >= i2:
return True # we have checked the whole string
# get a pair of chars
c1 = word[i1]
c2 = word[i2]
if _symd[c1] != c2:
return False # the pair wasn't symmetric
i1 += 1
i2 -= 1
# note, added a space to list of chars to filter to a space
_filter_to_space = ",.?!:'\/ "
def _filter_ch(ch):
if ch in _filter_to_space:
return ' ' # return a space
elif ch in alpha1:
return ch # it's an alphabet letter so return it
else:
# It's something we don't want. Return empty string.
return ''
def clean(text):
return ''.join(_filter_ch(ch) for ch in text.lower())
def symmetrics(text):
# filter text: keep only chars in the alphabet or spaces
for word in clean(text).split():
if is_symmetric_word(word):
# use of yield makes this a generator.
yield word
lst = list(symmetrics("The boy...is a yob."))
print(lst) # prints: ['boy', 'a', 'yob']
No need to type the alphabet twice; we can reverse the first one.
We can make a dictionary that pairs each letter with its symmetric letter. This will make it very easy to test whether any given pair of letters is a symmetric pair. The function zip() makes pairs from two sequences; they need to be the same length, but since we are using a string and a reversed copy of the string, they will be the same length.
It's best to write a simple function that does one thing, so we write a function that does nothing but check if a string is symmetric. If you give it a zero-length string it returns False, otherwise it sets i1 to the first character in the string and i2 to the last. It compares characters as long as they continue to be symmetric, and increments i1 while decrementing i2. If the two meet or pass each other, we know we have seen the whole string and it must be symmetric, in which case we return True; if it ever finds any pair of characters that are not symmetric, it returns False. We have to do the check for whether i1 and i2 have met or passed at the top of the loop, so it won't try to check if a character is its own symmetric character. (A character can't be both 'a' and 'z' at the same time, so a character is never its own symmetric character!)
Now we write a wrapper that filters out the junk, splits the string into words, and tests each word. Not only does it convert the chosen punctuation characters to spaces, but it also strips out any unexpected characters (anything not an approved punctuation char, a space, or a letter). That way we know nothing unexpected will get through to the inner function. The wrapper is "lazy"... it is a generator that yields up one word at a time, instead of building the whole list and returning that. It's easy to use list() to force the generator's results into a list. If you want, you can easily modify this function to just build a list and return it.
If you have any questions about this, just ask.
EDIT: The original version of the code didn't do the right thing with the punctuation characters; this version does. Also, as #heltonbiker suggested, why type the alphabet when Python has a copy of it you can use? So I made that change too.
EDIT: #heltonbiker's change introduced a dependency on Python version! I left it in with a suitable try:/except block to handle the problem. It appears that Python 3.x has improved the name of the lowercase ASCII alphabet to string.ascii_lowercase instead of plain string.lowercase.

Categories