Taking a string as input and convert the letters? - python

I want to take a string as input and convert the even letters to uppercase and odd letters to lowercase. Is there any other way to do it?
def myfunc(word):
result = ''
index = 0
for letter in word:
if index % 2 ==0:
result += letter.lower()
else:
result +=letter.upper()
index += 1
return result

There's many more interesting ways to skin this cat, but here's an abbreviated one:
def myfunc(word):
action = {0: str.lower, 1: str.upper}
return ''.join([action[index % 2](letter)
for index, letter in enumerate(word)])
print(myfunc('hello world'))

Many, many different ways to do it but here's one you might like. It's a little more concise and one might argue a bit more 'Pythonic'.
def myfunc(word):
# Convert everything to lowercase
word = word.lower()
# Convert to list of individual letters
letters = list(word)
# Convert every other character starting from the 2nd
# to uppercase
for i in range(1, len(word) + 1, 2):
letters[i] = letters[i].upper()
# Convert back to string
return ''.join(letters)

It looks like there's a more pythonic solution provided, so I'd like to give a slightly less pythonic solution for variety.
def funnyCase(word):
i = 0
# convert everything to lowercase
word = word.lower()
# convert to a list of characters
letters = list(word)
# make it funny
for letter in letters:
# This is bitwise XOR. It's a different way to check even/odd.
if i^1==0:
letters[i] = letter.upper()
i++
return ''.join(letters)
I do hope you understand John Gordon's comment about multiple solutions. This is the beauty of programming - there is no one way to do things.
Edit - I messed up. Happens

Related

Implementing the Caesar cipher algorithm in Python

So I'm trying to do a code that will shift every letter in a word back by a number of letters in the alphabet (wrapping around for the end). For example, if I want to shift by 2 and I input CBE, I should get AZC. or JOHN into HMFL. I got a code to work for only one letter, and I wonder if there's a way to do a nested for loop for python (that works?)
def move(word, shift):
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"
original = ""
for letter in range(26, len(alphabet)):
if alphabet[letter] == word: #this only works if len(word) is 0, I want to be able to iterate over the letters in word.
original += alphabet[letter-shift]
return original
You could start like this
def move(word, shift):
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
return "".join([alphabet[alphabet.find(i)-shift] for i in word])
Basically, this list comprehension constructs a list of the single letters. Then, the index of the letter in the alphabet is found by the .find method. The (index - shift) is the desired new index, which is extracted from alphabet. The resulting list is joined again and returned.
Note that it does obviously not work on lowercase input strings (if you want that use the str.upper method). Actually, the word should only consist of letters present in alphabet. For sentences the approach needs to treat whitespaces differently.
Don't find the letter in the alphabet that way -- find it with an index operation. Let char be the letter in question:
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
...
char_pos = alphabet.index(char)
new_pos = (char_pos - shift) % len(alphabet)
new_char = alphabet[new_pos]
Once you understand this, you can collapse those three lines to a single line.
Now, to make it operate on an entire word ...
new_word = ""
for char in word:
# insert the above logic
new_word += new_char
Can you put all those pieces together?
You'll still need your check to see that char is a letter. Also, if you're interested, you can build a list comprehension for all the translated characters and the apply ''.join() to get your new word.
For instance ...
If the letter is in the alphabet (if char in alphabet), shift the given distance and get the new letter, wrapping around the end if needed (% 26). If it's not a capital letter, then use the original character.
Make a list from all these translations, and then join them into a string. Return that string.
def move(word, shift):
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
return ''.join([alphabet[(alphabet.find(char) - shift) % 26]
if char in alphabet else char
for char in word])
print move("IBM", 1)
print move("The 1812 OVERTURE is COOL!", 13)
Output:
HAL
Ghe 1812 BIREGHER is PBBY!
A_VAL = ord('a')
def move(word, shift):
new_word = ""
for letter in word:
new_letter = ord(letter) - shift
new_word += chr(new_letter) if (new_letter >= A_VAL) else (26 + new_letter)
return new_word
Note that this will only work for lowercase words. As soon as you start mixing upper and lowercase letters you'll need to start checking for them. But this is a start. I discarded your nested loop idea because you should avoid those if at all possible.
You could use : chr() give you the character for a ascii number, ord() give you the ascii number for the matching character.
Here is an old Vigenere project :
def code_vigenere(ch,cle):
text = ch.lower()
clef = cle.lower()
L = len(cle)
res = ''
for i,l in enumerate(text):
res += chr((ord(l) - 97 + ord(cle[i%L]) - 97)%26 +97)
return res

Fastest way to determine if two strings differ by a single letter in a large set of strings

I am trying to compare two strings and add one of the strings to a list if they are almost equal (differ by a single letter). What would be the fastest way to do this as my set of words is over 90k and doing this often takes too long?
EDIT: one of the words (comparison_word in code below) does not change.
EDIT2: the words must be of equal length
This is my current code:
for word in set_of_words:
amount = 0
if len(word) == len(comparison_word):
for i in range(len(word)):
if comparison_word[i] != word[i]:
amount += 1
if amount == 1:
list_of_words.append(word)
return list_of_words
You might find zip is a more efficient than indexing:
def almost_equal(set_of_words,comp):
ln = len(comp)
for word in set_of_words:
count = 0
if len(word) == ln:
for a, b in zip(word, comp):
count += a != b
if count == 2:
break
else:
yield word
Demo:
In [5]: list(almost_equal(["foo","bar","foob","foe"],"foa"))
Out[5]: ['foo', 'foe']
The following searches my dictionary of 61K words in about 25 msec.
import re
def search(word, text):
ws = [r'\b{}[^{}]{}\b'.format(w[:i],w[i],w[i+1:]) for i in range(len(word))]
for mo in re.finditer('|'.join(ws), text):
yield mo.group()
with open("/12dicts/5desk.txt") as f:
text = f.read()
for hit in search('zealoos', text):
print(hit) #prints zealous
Presuming that the list of strings is in a file, one string per line, read it in as one long string and use a regular expression to search the string for matches.
search() takes a word like 'what' and turns it into a regular expression like this:
\b[^w]hat\b|\bw[^h]at\b|\bwh[^a]t\b|\bwha[^t]\b
And then scans all the words and find all the near misses--at C-speed.
The idea is to reduce the amount of work being done:
n_comparison_word = len(comparison_word)
for word in set_of_words:
amount = 0
n_word = len(word)
if n_word != n_comparison_word:
continue
for i in range(n_word):
if comparison_word[i] != word[i]:
amount += 1
if amount == 2:
break
if amount == 1:
list_of_words.append(word)
return list_of_words
Some notes:
The value of len(comparison_word) needs to be computed only once (ever).
The value of len(word) needs to computed once (per iteration of the loop).
You know you can stop looking at a word when amount reaches the value 2 (or more - in any case that word can no longer be part of the result).
It may be worth reading this part of the Python documentation regarding the continue and break statements which are both used in the code.
Haven't done exhaustive testing, but if comparison_word is not too long (fewer than 6 letters), and your set_of_words can change, then it might be worth it to compute all acceptable words, store those in a set, a simply iterate through set_of_words and test for word in acceptable_words.
If not, here's my take on your code:
for word in set_of_words:
different_letter_exists = False
length = len(word)
if length == len(comparison_word):
for i, letter in enumerate(word):
if letter != comparison_word[i]:
if different_letter_exists:
break
else:
different_letter_exists = True
if i == length:
list_of_words.append(word)
Essentially: for every word, once you encounter an different letter, different_letter_exists is set to True. If you encounter it again, you break out of the loop. A new word is only added if i == length, which only happens if enumerate gets all the way to the end, which only happens if only one different letter exists.
Good luck :)

Cannot remove two vowels in a row

I have to enter a string, remove all spaces and print the string without vowels. I also have to print a string of all the removed vowels.
I have gotten very close to this goal, but for some reason when I try to remove all the vowels it will not remove two vowels in a row. Why is this? Please give answers for this specific block of code, as solutions have helped me solve the challenge but not my specific problem
# first define our function
def disemvowel(words):
# separate the sentence into separate letters in a list
no_v = list(words.lower().replace(" ", ""))
print no_v
# create an empty list for all vowels
v = []
# assign the number 0 to a
a = 0
for l in no_v:
# if a letter in the list is a vowel:
if l == "a" or l == "e" or l == "i" or l == "o" or l == "u":
# add it to the vowel list
v.append(l)
#print v
# delete it from the original list with a
del no_v[a]
print no_v
# increment a by 1, in order to keep a's position in the list moving
else:
a += 1
# print both lists with all spaces removed, joined together
print "".join(no_v)
print "".join(v)
disemvowel(raw_input(""))
Mistakes
So there are a lot of other, and perhaps better approaches to solve this problem. But as you mentioned I just discuss your failures or what you can do better.
1. Make a list of input word
There are a lot of thins you could do better
no_v = list(words.lower().replace(" ", ""))
You don't replaces all spaces cause of " " -> " " so just use this instead
no_v = list(words.lower().translate( None, string.whitespace))
2. Replace for loop with while loop
Because if you delete an element of the list the for l in no_v: will go to the next position. But because of the deletion you need the same position, to remove all the vowels in no_v and put them in v.
while a < len(no_v):
l = no_v[a]
3. Return the values
Cause it's a function don't print the values just return them. In this case replace the print no_v print v and just return and print them.
return (no_v,v) # returning both lists as tuple
4. Not a mistake but be prepared for python 3.x
Just try to use always print("Have a nice day") instead of print "Have a nice day"
Your Algorithm without the mistakes
Your algorithm now looks like this
import string
def disemvowel(words):
no_v = list(words.lower().translate( None, string.whitespace))
v = []
a = 0
while a < len(no_v):
l = no_v[a]
if l == "a" or l == "e" or l == "i" or l == "o" or l == "u":
v.append(l)
del no_v[a]
else:
a += 1
return ("".join(no_v),"".join(v))
print(disemvowel("Stackoverflow is cool !"))
Output
For the sentence Stackoverflow is cool !\n it outputs
('stckvrflwscl!', 'aoeoioo')
How I would do this in python
Not asked but I give you a solution I would probably use. Cause it has something to do with string replacement, or matching I would just use regex.
def myDisemvowel(words):
words = words.lower().translate( None, string.whitespace)
nv = re.sub("[aeiou]*","", words)
v = re.sub("[^a^e^i^o^u]*","", words)
return (nv, v)
print(myDisemvowel("Stackoverflow is cool !\n"))
I use just a regular expression and for the nv string I just replace all voewls with and empty string. For the vowel string I just replace the group of all non vowels with an empty string. If you write this compact, you could solve this with 2 lines of code (Just returning the replacement)
Output
For the sentence Stackoverflow is cool !\n it outputs
('stckvrflwscl!', 'aoeoioo')
You are modifying no_v while iterating through it. It'd be a lot simpler just to make two new lists, one with vowels and one without.
Another option is to convert it to a while loop:
while a < len(no_v):
l = no_v[a]
This way you have just a single variable tracking your place in no_v instead of the two you currently have.
For educational purposes, this all can be made significantly less cumbersome.
def devowel(input_str, vowels="aeiou"):
filtered_chars = [char for char in input_str
if char.lower() not in vowels and not char.isspace()]
return ''.join(filtered_chars)
assert devowel('big BOOM') == 'bgBM'
To help you learn, do the following:
Define a function that returns True if a particular character has to be removed.
Using that function, loop through the characters of the input string and only leave eligible characters.
In the above, avoid using indexes and len(), instead iterate over characters, as in for char in input_str:.
Learn about list comprehensions.
(Bonus points:) Read about the filter function.

Return Boolean depending on whether or not string is in alphabetical order

This is what I have so far in python:
def Alphaword():
alphabet = "abcdefghijklmnopqrstuvwxyz"
x = alphabet.split()
i = 0
word = input("Enter a word: ").split()
I'm planning on using a for loop for this problem but not sure how to start it.
Think about it this way - a word containing letters (if they're in alphabetical order) should be equal to itself when forced to be in alphabetical order, so:
def alpha_word():
word = list(input('Enter a word: '))
return word == sorted(word)
That's the naive approach anyway... if you had massive iterables of sequences, there are other techniques, but for strings typed via input, it's practical enough.
There are two ways:
You just loop over each char in the string, use a test like if a[i]<a[i+1]. This works because 'a' < 'b' is true.
You can split string to char list, sort it and compare it to the original list.
Python supports direct comparison of characters. For instance, 'a' < 'b' < 'c' and so on. Use a for loop to go through each letter in the word and compare it to the previous letter:
def is_alphabetical(word):
lowest = word[0]
for letter in word:
if letter >= lowest:
lowest = letter
else:
return False
return True

Small issue with Palindrome program

I've been working on this Palindrome program and am really close to completing it.Close to the point that it's driving me a bit crazy haha.
The program is supposed to check each 'phrase' to determine if it is a Palindrome or not and return a lowercase version with white space and punctuation removed if it is in fact a Palindrome. Otherwise, if not, it's supposed to return None.
I'm just having an issue with bringing my test data into the function. I can't seem to think of the correct way of dealing with it. It's probably pretty simple...Any ideas?
Thanks!
import string
def reverse(word):
newword = ''
letterflag = -1
for numoletter in word:
newword += word[letterflag]
letterflag -= 1
return newword
def Palindromize(phrase):
for punct in string.punctuation:
phrase= phrase.replace(punct,'')
phrase = str(phrase.lower())
firstindex = 0
secondindex = len(phrase) - 1
flag = 0
while firstindex != secondindex and firstindex < secondindex:
char1 = phrase[firstindex]
char2 = phrase[secondindex]
if char1 == char2:
flag += 1
else:
break
firstindex += 1
secondindex -= 1
if flag == len(phrase) // (2):
print phrase.strip()
else:
print None
def Main():
data = ['Murder for a jar of red rum',12321, 'nope', 'abcbA', 3443, 'what',
'Never odd or even', 'Rats live on no evil star']
for word in data:
word == word.split()
Palindromize(word)
if __name__ == '__main__':
Main()
Maybe this line is causing the problems.
for word in data:
word == word.split() # This line.
Palindromize(word)
You're testing for equality here, rather than reassigning the variable word which can be done using word = word.split(). word then becomes a list, and you might want to iterate over the list using
for elem in word:
Palindromize(elem)
Also, you seem to be calling the split method on int, which is not possible, try converting them to strings.
Also, why do you convert the phrase to lower case in the for loop, just doing it once will suffice.
At the "core" of your program, you could do much better in Python, using filter for example. Here is a quick demonstration:
>>> phrase = 'Murder for a jar of red rum!'
>>> normalized = filter(str.isalnum, phrase.lower())
>>> normalized
'murderforajarofredrum'
>>> reversed = normalized[-1::-1]
>>> reversed
'murderforajarofredrum'
# Test is it is a palindrome
>>> reversed == normalized
True
Before you go bananas, let's rethink the problem:
You have already pointed out that Palindromes only make sense in strings without punctuation, whitespace, or mixed case. Thus, you need to convert your input string, either by removing the unwanted characters or by picking the allowed ones. For the latter, one can imagine:
import string
clean_data = [ch for ch in original_data if ch in string.ascii_letters]
clean_data = ''.join(clean_data).lower()
Having the cleaned version of the input, one might consider the third parameter in slicing of strings, particularly when it's -1 ;)
Does a comparison like
if clean_data[::-1] == clean_data:
....
ring a bell?
One of the primary errors that i spotted is here:
for word in data:
word==word.split()
Here, there are two mistakes:
1. Double equals make no point here.
2. If you wish to split the contents of each iteration of data, then doing like this doesn't change the original list, since you are modifying the duplicate set called word. To achieve your list, do:
for i in range(data):
data[i]=data[i].split()
This may clear your errors

Categories