Using python to determine if two strings are opposites given conditions - python

If we had certain conditions to fulfill such as
a is the opposite of b
c is the opposite of h
l is the opposite of r
the opposite of the string acl would be bhr
Do you guys think you can help me figure out how I would go about building a function that when given two strings will return a boolean that lets me know whether the given strings are opposite. for example input: opposite("ahl","bcr") would return True while ("ahl","bbr") would return False.

I would do it like a string compare, except that for every character, there would be a lookup table to get a translated value like so:
lookup = {
'a': 'b',
'c': 'h',
'l': 'r'
}
def opposite(one, two):
count = 0
if len(one) != len(two):
return False
for i in one:
if (lookup[i] != two[count] ):
return False
count += 1
return True
if __name__ == '__main__':
print opposite('acl', 'bhr')

If you have the lookup table, then this is a one-liner.
lookup = {
'a': 'b',
'c': 'h',
'l': 'r'
}
def opposite(str1, str2):
return [ lookup[c] for c in str1] == [ c for c in str2 ]
Depending on the actual situation (whether you know the first string contains only "acl" and the second only their opposites), you may need to have bidirectional lookup:
lookup = {
'a': 'b',
'c': 'h',
'l': 'r',
'b': 'a',
'h': 'c',
'r': 'l'
}
And if you want to have an exception raised when there are invalid characters in the input, you may change the function:
def opposite(str1, str2):
return [ lookup[c] for c in str1] == [ lookup[lookup[c]] for c in str2 ]

Related

"list index out of range" error when setting a string variable in for-loop instead of string directly?

I am just trying to loop through certain letters in a json-message and then extract a new json-message out of it and it doesn't work but it should!
json_message = ast.literal_eval( f"{ { 'B': [{'a': 'A', 'f': '1', 'l': '0.00000000'}, {'a': 'B', 'f': '2', 'l': '0.00000000'}, {'a': 'C', 'f': '3', 'l': '0.00000000'}]} }" )
currency = ["'A'", "'C'"]
for x in range(len(currency)):
aaa = currency[x]
print(aaa)
gives
'A'
So, aaa = 'A', right? Therefore, this should work:
msg = [y for y in json_message['B'] if y['a'] == aaa]
print(msg[0])
but it gives
IndexError: list index out of range
because
print(msg)
[]
However, when I do
msg = [y for y in json_message['B'] if y['a'] == 'A']
then it works:
print(msg[0])
{'a': 'A', 'f': '1', 'l': '0.00000000'}
Question: Why doesn't it work and how can I fix it?
aaa includes the single quotes as part of the string itself. The line that doesn't work is equivalent to
msg = [y for y in json_message['B'] if y['a'] == "'A'"]
You can see this if you do print(repr(aaa)), which (for most objects) prints the code you would type to get that exact object. So print(repr('"foo"')) would print '"foo"', rather than "foo".
To fix it, you can either remove the single quotes from the strings in currency, or if those are coming from some code outside your control, pass them through ast.literal_eval() as well.

I need to encode a message from an input in Python

So i have to encode a message but it's a different encoding, if the input is CAT the output must be DQ6 it's supposed to encode changing every letter of the input into the upper left key on the keyboard, for example again: in: bear out: G3Q4. I tried to code this in dictionaries like this:
d1 = {"q": 1,"Q": 1,"w": 2,"W": 2,"e": 3,"E": 3,"r": 4,"R": 4,"t": 5,"T": 5,"y": 6,"Y": 6,"u": 7,"U": 7,"i": 8,"I": 8,"o": 9,"O": 9,"p": 0,"P": 0}
d2 = {"a": 'Q',"A": 'Q',"s": 'W',"S": 'W',"d": 'E',"D": 'E',"f": 'R',"F": 'R',"g": 'T',"G": 'T',"h": 'Y',"H": 'Y',"j": 'U',"J": 'U',"k": 'I',"K": 'I',"l": 'O',"L": 'O',"ñ": 'P',"Ñ": 'P'}
d3 = {"z": 'A',"Z": 'A',"x": 'S',"X": 'S',"c": 'D',"C": 'D',"v": 'F',"V": 'F',"b": 'G',"B": 'G',"n": 'H', "N": 'H',"m": 'J',"M": 'J',",": 'K',".": 'L',"-": 'Ñ'}
I tried this function to check for every key but everything i'm getting is "None" as the value.
text = input("Text: ")
def cif(text):
cifrd = ""
for i in text:
if i in d1:
cifrd += d1[(d1.index(i))%(len(d1))]
elif i in d2:
cifrd += d2[(d2.index(i))%(len(d2))]
elif i in d3:
cifrd += d3[(d3.index(i))%(len(d3))]
else:
cifrd += i
print("New text: ",cif(cifrd))
Appreciate any help.
Your encoding:
d1 = {"q": 1,"Q": 1,"w": 2,"W": 2,"e": 3,"E": 3,"r": 4,"R": 4,"t": 5,"T": 5,"y": 6,"Y": 6,"u": 7,"U": 7,"i": 8,"I": 8,"o": 9,"O": 9,"p": 0,"P": 0}
d2 = {"a": 'Q',"A": 'Q',"s": 'W',"S": 'W',"d": 'E',"D": 'E',"f": 'R',"F": 'R',"g": 'T',"G": 'T',"h": 'Y',"H": 'Y',"j": 'U',"J": 'U',"k": 'I',"K": 'I',"l": 'O',"L": 'O',"ñ": 'P',"Ñ": 'P'}
d3 = {"z": 'A',"Z": 'A',"x": 'S',"X": 'S',"c": 'D',"C": 'D',"v": 'F',"V": 'F',"b": 'G',"B": 'G',"n": 'H', "N": 'H',"m": 'J',"M": 'J',",": 'K',".": 'L',"-": 'Ñ'}
There are a few issues. See my comments
text = input("Text: ")
def cif(text):
cifrd = ""
for letter in text:
# There is no need to manually write out each dictionary and do a check
# Put the dictionaries in a list, iterate over each one, and if the letter
# is in the dictionary, you will get the respective letter back
for encode in [d1, d2, d3]:
# check if my letter is in the dictionary
actual = encode.get(letter)
# If you check a dictionary and the key is not there, you will get `None`, this if statement ensures you only append actual number/characters
if actual:
cifrd += str(actual)
# When using a function, return something if you need it outside of the function
return cifrd
decoded = cif(text)
print("New text: {}".format(decoded))
There are a number of issues with your code:
You need to return the "encoded" text at the end of the cif() function
You need to pass the text variable to the cif() function, not cifrd which isn't defined outside your function
Dictionaries do not have an .index() method, you access dictionary items by key, e.g., d1["q"] returns 1.
For what it's worth, there's no need to maintain three separate dictionaries, nor is there reason to maintain both lower- and upper-cased letters in your dictionary; store lower-cased or upper-cased keys, and transform the input to the correct case when accessing the translation, i.e., input "Q" -> lowercase "q" -> d1["q"].
Here:
mapping = {'q': 1, 'w': 2, 'e': 3, 'r': 4, 't': 5, 'y': 6, 'u': 7, 'i': 8, 'o': 9, 'p': 0,
'a': 'q', 's': 'w', 'd': 'e', 'f': 'r', 'g': 't', 'h': 'y', 'j': 'u', 'k': 'i', 'l': 'o', 'ñ': 'p',
'z': 'a', 'x': 's', 'c': 'd', 'v': 'f', 'b': 'g', 'n': 'h', 'm': 'j', ',': 'k', '.': 'l', '-': 'ñ'}
def cif(s: string) -> string:
encoded_string = ""
for char in s:
encoded_string += mapping.get(char.lower(), char) # leaves the character un-encoded, if the character does not have a mapping
return encoded string
I would actually suggest using str.translate(). You can pass two strings, the first being the input characters, the second being the characters to which those inputs should map:
t = str.maketrans("qwertyuiopasdfghjklñzxcvbnm,.-", "1234567890qwertyuiopasdfghjklñ")
"hello world".translate(t)
'y3oo9 294oe'

Converting mobile numeric keypad numbers to its corresponding word Python

I need to create a function, where, If I give an input like 999933. It should give output as "ze". It basically work as numeric mobile phone keypad. How can I this. I have searched to get some sample in internet. All, I got was quite opposite. Like, Giving the text as input and you will get the number. I couldn't get the exact flow of, how to achieve that. Please let me know, how can i do that.
def number_to_text(val):
pass
Create a mapping pad_number to letter.
Use itertools.groupby to iterate over consecutive pad presses and calculate which letter we get.
import itertools
letters_by_pad_number = {"3": "def", "9": "wxyz"}
def number_to_text(val):
message = ""
# change val to string, so we can iterate over digits
digits = str(val)
# group consecutive numbers: itertools.groupby("2244") -> ('2', '22'), ('4','44')
for digit, group in itertools.groupby(digits):
# get the pad letters, i.e. "def" for "3" pad
letters = letters_by_pad_number[digit]
# get how many consecutive times it was pressed
presses_number = len(list(group))
# calculate the index of the letter cycling through if we pressed
# more that 3 times
letter_index = (presses_number - 1) % len(letters)
message += letters[letter_index]
return message
print(number_to_text(999933))
# ze
And hardcore one-liner just for fun:
letters = {"3": "def", "9": "wxyz"}
def number_to_text(val):
return "".join([letters[d][(len(list(g)) - 1) % len(letters[d])] for d, g in itertools.groupby(str(val))])
print(number_to_text(999933))
# ze
The other answers are correct, but I tried to write a less brief more real world (including doctests) explanation of how the previous results worked:
dialpad_text.py:
# Import the groupby function from itertools,
# this takes any sequence and returns an array of groups by some key
from itertools import groupby
# Use a dictionary as a lookup table
dailpad = {
'2': ['a', 'b', 'c'],
'3': ['d', 'e', 'f'],
'4': ['g', 'h', 'i'],
'5': ['j', 'k', 'l'],
'6': ['m', 'n', 'o'],
'7': ['p', 'q', 'r', 's'],
'8': ['t', 'u', 'v'],
'9': ['w', 'x', 'y', 'z'],
}
def dialpad_text(numbers):
"""
Takes in either a number or a string of numbers and creates
a string of characters just like a nokia without T9 support
Default usage:
>>> dialpad_text(2555222)
'alc'
Handle string inputs:
>>> dialpad_text('2555222')
'alc'
Handle wrapped groups:
>>> dialpad_text(2222555222)
'alc'
Throw an error if an invalid input is given
>>> dialpad_text('1BROKEN')
Traceback (most recent call last):
...
ValueError: Unrecognized input "1"
"""
# Convert to string if given a number
if type(numbers) == int:
numbers = str(numbers)
# Create our string output for the dialed numbers
output = ''
# Group each set of numbers in the order
# they appear and iterate over the groups.
# (eg. 222556 will result in [(2, [2, 2, 2]), (5, [5, 5]), (6, [6])])
# We can use the second element of each tuple to find
# our index into the dictionary at the given number!
for number, letters in groupby(numbers):
# Convert the groupby group generator into a list and
# get the offset into our array at the specified key
offset = len(list(letters)) - 1
# Check if the number is a valid dialpad key (eg. 1 for example isn't)
if number in dailpad.keys():
# Add the character to our output string and wrap
# if the number is greater than the length of the character list
output += dailpad[number][offset % len(dailpad[number])]
else:
raise ValueError(f'Unrecognized input "{number}"')
return output
Hope this helps you understand what's going on a lower level! Also if you don't trust my code, just save that to a file and run python -m doctest dialpad_text.py and it will pass the doctests from the module.
(Notes: without the -v flag it won't output anything, silence is golden!)
You need to
group the same digits together with the regex (\d)\1* that capture a digit then the same digit X times
use the value of a digit in the group to get the key
use the length of it to get the letter
phone_letters = ["", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"]
def number_to_text(val):
groups = [match.group() for match in re.finditer(r'(\d)\1*', val)]
result = ""
for group in groups:
keynumber = int(group[0])
count = len(group)
result += phone_letters[keynumber][count - 1]
return result
print(number_to_text("999933")) # ze
Using list comprehension
def number_to_text(val):
groups = [match.group() for match in re.finditer(r'(\d)\1*', val)]
return "".join(phone_letters[int(group[0])][len(group) - 1] for group in groups)
A slightly Modified answer of RaFalS without using itertools
import itertools
from collections import defaultdict
letters_by_pad_number = {"3": "def", "9": "wxyz"}
val = 999933
message = ""
digits = str(val)
num_group = defaultdict(int)
for digit in digits:
num_group[digit] += 1
for num in num_group.keys():
message += letters_by_pad_number[num][num_group[num]-1]
print(message)
# ze

How to use Run Length Encoding in Python the recursive ways

I want to make a Run Length encoding but using recursive ways for some reason, but I can't figure out how to transfer my code from looping to recursive. This is for python. This is looping one, I really want to make it to recursive.
def runLengthEncoding(words):
mylist=[]
count=1
for i in range(1,len(words)):
if words[i] == words[i-1]:
count=count+1
else:
mylist.append(words[i-1])
mylist.append(count)
count=1
if words:
mylist.append(words[-1])
mylist.append(count)
return mylist
I expect the answer ['A', 7, 'B', 3, 'C', 1, 'E', 1, 'Z', 1] for runLengthEncoding("AAAAAAABBBCEZ"). Just like the answer from the last code. but I just want to change the code to recursive ways.
What about builtin function?
from collections import Counter
letter_counter = Counter(list("AAAAAAABBBCEZ"))
print(dict(letter_counter))
result is {'A': 7, 'B': 3, 'C': 1, 'E': 1, 'Z': 1}
This could have solved easily by other methods but since you are particular about recursive solution and a list form in the end, here is my solution.
String = "AAAAAAABBBCEZ"
Global_List = []
StartWord = String[0]
Count = 0
def RecursiveLength(String):
global Global_List
global StartWord
global Count
if len(String)==0:
Global_List.append(StartWord)
Global_List.append(Count)
return
else:
if String[0] == StartWord:
Count += 1
String = String[1:]
return RecursiveLength(String)
else:
Global_List.append(StartWord)
Global_List.append(Count)
StartWord = String[0]
Count = 1
String = String[1:]
return RecursiveLength(String)
RecursiveLength(String)
print(Global_List)
This gave me the following output. However there are better ways than recursion to solve this.
['A', 7, 'B', 3, 'C', 1, 'E', 1, 'Z', 1]
All the best
You'd better put the result into a dictionary. And you can use str.count() to calculate the number of "char" in a string. Code is as below:
data = "AAAAAAABBBCEZ"
# delet the duplicated characters in data
chrs = ''
chrs = [chrs + x for x in data if x not in chrs]
res = {chr: data.count(chr) for chr in chrs}
print(res)
output
{'A': 7, 'B': 3, 'C': 1, 'E': 1, 'Z': 1}

building a boolean dictionary from 2 lists in python

I'm trying to make a dictionary with values 'True' or 'False' when comparing elements in 2 lists. This is probably a bit basic but I'm new to coding and I don't understand why it always assigns the 'True' value even though I can see its not true:
letters = [A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z]
randomData = []
f = open('randomData.txt', 'r')
for line in f:
randomData.append(line.rstrip().split()[0])
f.close()
The 'randomData.txt' file looks like:
A'\t'0003'\t'0025'\t'chr1
B'\t'0011'\t'0021'\t'chr7
D'\t'0043'\t'0068'\t'chr3
F'\t'0101'\t'0119'\t'chr7
The randomData list should now look like:
['A','B','D','F']
I tried:
sameLetters = {}
i=0
while i < len(letters):
if letters[i] and randomData:
#append to dictionary
sameLetters[letters[i]] = 'True'
else:
#append to dictionary
sameLetters[letters[i]] = 'False'
i=i+1
print sameLetters
I expected something like:
{'A': 'True', 'B': 'True', 'C': 'False', 'D': 'True', 'E': 'False', 'F': 'True', 'G': 'False', etc
Instead all values in the dictionary are 'True'. Can anyone see the problem? Or give any pointers or explanations? Any help would be great, many thanks.
Perhaps you meant if letters[i] in randomData
I think you want to do something like:
sameLetters = {l: l in randomData for l in letters}
Your current attempt doesn't work because you check
if letters[i] and randomData:
# ^ should be in
and Python interprets both non-empty strings (letters[i]) and non-empty lists (randomData) as True.
Also, note that letters is already available in Python:
from string import ascii_uppercase
This is a string, but you can iterate through and index a string just like a list, and in will still work.
Seems like you only care about which letter appears in your random data, so why not use a set?
from string import ascii_uppercase
randomData = ['A', 'B', 'D', 'F', 'A']
appeared = set(ascii_uppercase).intersection(set(randomData))
print appeared
And later you can us it like this:
char = 'z'
if char in appeared:
print 'yes'
else:
print 'no'
EDIT:
Then how about this:)
from string import ascii_uppercase
randomData = ['A', 'B', 'D', 'F', 'A']
appeared = set(ascii_uppercase).intersection(set(randomData))
d = dict(zip(ascii_uppercase, (False,) * 26))
for key in appeared:
d[key] = True
print d

Categories