List Comprehension in a function - python

So I'm trying to write a function for a hangman game that will return a string that is composed of lowercase English letters - all lowercase English letters that are not in lettersGuessed. I can't see to get the list comprehension to work
def getAvailableLetters(lettersGuessed):
'''
lettersGuessed: list, what letters have been guessed so far
returns: string, comprised of letters that represents what letters have not
yet been guessed.
'''
[letterGuessed.remove(letter) if letter in'abcdefghijklmnopqrstuvwxyz' for letter in lettersGuessed ]

[letter for letter in 'abcdefghijklmnopqrstuvwxyz' if letter not in lettersGuessed]
To give more of an explanation as to why this works, it helps to consider and list comprehension as the following:
[ expression for item in list if conditional ]
In our case, list is the letters of the alphabet.
Our expression is simply the individual letter in the list and our conditional is if the letter does not already exist in lettersGuessed.
The nice thing is that it almost translates into an english sentence which should make it easy to understand.
Give me each letter in the alphabet if the letter is not in the list of guessed letters
I would recommend having a read through this article as having a good understanding of list comprehensions will be a huge benefit for your python coding.
https://www.pythonforbeginners.com/basics/list-comprehensions-in-python

Kind of expanding on #scoJo's response, here's my take.
import string
test_guesses = ['a','b','f']
def getAvailableLetters(lettersGuessed):
# Note that we're ensuring that input letters are lowercase when being compared to the list.
return ''.join([i for i in string.ascii_lowercase if i.lower() not in lettersGuessed])
With the original response, you were removing letters from the letters guessed list, not the alphabet.
My solution also taps into the string standard library of Python to create a list of letters without having to ensure that you've typed each one.
If you want to return a list, just remove the .join() function.
Input:
getAvailableLetters(test_guesses)
Output:
'cdeghijklmnopqrstuvwxyz'

For simplicity i'd propose using Python sets
A set is an "unordered collection of unique elements"
def getAvailableLetters(lettersGuessed):
# convert to set
all_letters_set = set('abcdefghijklmnopqrstuvwxyz')
letters_guessed_set = set(lettersGuessed)
# substract sets
available_letters = list(all_letters_set - letters_guessed_set)
# convert list of str to single str
return ''.join(available_letters)
This way you can do a simple subtraction to retrieve the list of available letters and then join this list to a single string.
No manual iteration needed
Note: if you want to preserve the sorted order of the letters still available, use Pythons sort function before returning the string
available_letters.sort()

Related

Python: How do you make a list start with the lowercase strings?

I'm currently making a simple code that allows the user to input a sentence or a group of words and the program will sort the words alphabetically. This initial code works great but unfortunately, it prints out the uppercase words first and then the lowercase words. I was hoping if there was a simple way to reverse this and have the lowercase words go first on the list instead of the uppercase words. Any help would be greatly appreciated!
strings = input("Enter words: ")
words = strings.split()
words.sort()
print("The sorted words are:")
for word in words:
print(word)
You can pass a custom key function to sort with.
words.sort(key=lambda s: (s[0].isupper(), s))
By default, Python sorts strings according to the ordinal value of each character (ord(c)), which corresponds to its ASCII value for English alphabet. In ASCII, the uppercase letters have lower value than lowercase letters, which is why you have this kind of result.
To modify this behavior, you need to modify the key in your sort function.
For example, if you wanted a case insensitive sorting, you could do the following:
words.sort(key=str.lower)
In your case, you need to implement two rules:
Case-insensitive sorting (e.g. 'alice' < 'bob' < 'dylan'): can be done with str.lower
Among words with same characters, but different case, put the uppercase version last (e.g. 'alice' < 'Alice' < 'ALICE'): can be done by reverting the original ASCII ordering with str.swapcase
You can implement these two rules at once with a key function that returns a tuple (rule1, rule2). More information about that here.
words.sort(key=lambda x: (x.lower(), x.swapcase()))
Here is my test code for reference:
words = ["Adam", "ADAM", "Zack", "Chloe", "Bernard", "SIGFRIED", "bErnard"]
words.sort(key=lambda x: (x.lower(), x.swapcase()))
print("The sorted words are:")
for word in words:
print(word)
Output:
The sorted words are:
Adam
ADAM
bErnard
Bernard
Chloe
SIGFRIED
Zack

Create new words from start word python

def make_new_words(start_word):
"""create new words from given start word and returns new words"""
new_words=[]
for letter in start_word:
pass
#for letter in alphabet:
#do something to change letters
#new_words.append(new_word)
I have a three letter word input for example car which is the start word.
I then have to create new word by replacing one letter at a time with every letter from the alphabet. Using my example car I want to create the words, aar, bar, car, dar, ear,..., zar. Then create the words car, cbr, ccr, cdr, cer,..., czr. Finally caa, cab, cac, cad, cae,..., caz.
I don't really know what the for loop should look like. I was thinking about creating some sort of alphabet list and by looping through that creating new words but I don't know how to choose what parts of the original word should remain. The new words can be appended to a list to be returned.
import string
def make_new_words(start_word):
"""create new words from given start word and returns new words"""
new_words = []
for i, letter in enumerate(start_word):
word_as_list = list(start_word)
for char in string.ascii_lowercase:
word_as_list[i] = char
new_words.append("".join(word_as_list))
return new_words
lowercase is just a string containing the lowercase letters...
We want to change each letter of the original word (here w) so we
iterate on the letters of w, but we'll mostly need the index of the letter, so we do our for loop on enumerate(w).
First of all, in python strings are immutable so we build a list x from w... lists are mutable
Now a second, inner loop on the lowercase letters: we change the current element of the x list accordingly (having changed x, we need to reset it before the next inner loop) and finally we print it.
Because we want to print a string rather than the characters in a list, we use the join method of the null string '' that glue together the elements of x using, of course, the null string.
I have not reported the output but it's exactly what you've asked for, just try...
from string import lowercase
w = 'car'
for i, _ in enumerate(w):
x = list(w)
for s in lowercase:
x[i] = s
print ''.join(x)
import string
all_letters = string.ascii_lowercase
def make_new_words(start_word):
for index, letter in enumerate(start_word):
template = start_word[:index] + '{}' + start_word[index+1:]
for new_letter in all_letters:
print template.format(new_letter)
You can do this with two loops, by looping over the word and then looping over a range for all letters. By keeping an index for the first loop, you can use a slice to construct your new strings:
for index in enumerate(start_word):
for let in range(ord('a'), ord('z')+1):
new_words.append(start_word[:index] + chr(let) + start_word[index+1:])
This could work as a brute-force approach, although you might end up with some performance issues when you go to try it with longer words.
It also sounds like you might want to constrain it only to words that exist in a dictionary at some point, which is a whole other can of worms.
But for right now, for three-letter words, you're onto something of the right track, although I worry that the question might be a little too specific for Stack Overflow.
First, you will probably have more success if you loop through the index for the word, rather than the letter:
alphabet = 'abcdefghijklmnopqrstuvwxyz'
for i in range(len(start_word)):
Then, you can use a slice to grab the letters before and after the index.
for letter in alphabet:
new_word = start_word[:i] + letter + start_word[i + 1:]
Another approach is given above, which casts the string to a list. That works around the fact that python will disallow simply setting start_word[i] = letter, which you can read about here.

Python temporarily change letter to lower case

I have a list that looks like this:
separate=[['I','wnet','to','the','mal,'],['and','bouht','a','new','shirt.'],['What','did','you','do','todya?']]
I'm going to run this through a program which will identify the misspelled words and replace them with the correct word but the list of words in the Webster Dictionary that I am using only uses lowercase letters. Can I temporarily change all of the letters to lowercase but then in the end return the original upper and lower case words?
I know about str.upper() and str.lower() and set.capitalize.
It seems like I want to use something like str.capwords() but inversely... I want to split the list into words(already done) and then make capital letters lower case.
Just use str.lower. It will return a new string with lower letter and keep the original string unchanged. So when you loop through the list such as if string.lower() in adict:, you do use the lower case to compare and the original string unchanged.
>>> a = 'ABC'
>>> a.lower()
'abc'
>>> a
'ABC'

Python - match letters of words in a list

I'm trying to create a simple program where a user enters a few letters
Enter letters: abc
I then want to run through a list of words I have in list and match and words that contain 'a','b', and 'c'.
This is what I've tried so far with no luck
for word in good_words: #For all words in good words list
for letter in letters: #for each letter inputed by user
if not(letter in word):
break
matches.append(word)
If you want all the letters inside the word:
[word for word in good_words if all(letter in word for letter in letters)]
The problem with your code is the break inside the inner loop. Python doesn't have a construction to allow breaking more than one loop at once (and you wanted that)
You could probably improve the spee using a Set or FrozenSet
If you look at the doc, it mentionned the case of testing membership :
A set object is an unordered collection of distinct hashable objects.
Common uses include membership testing, removing duplicates from a
sequence, and computing mathematical operations such as intersection,
union, difference, and symmetric difference.
List comprehensions are definitely the way to go, but just to address the issue that OP was having with his code:
Your break statement only breaks out of the innermost loop. Because of that the word is still appended to matches. A quick fix for this is to take advantage of python's for... else construct:
for word in good_words:
for letter in letters:
if letter not in word:
break
else:
matches.append(word)
In the above code, else only executes if the loop is allowed to run all the way through. The break statement exits out of the loop completely, and matches.append(..) is not executed.
import collections
I would first compute the occurrences of letters in the words list.
words_by_letters = collections.defaultdict(list)
for word in good_words:
key = frozenset(word)
words_by_letters[key].append(word)
Then it's simply a matter of looking for words with particular letter occurrences. This is hopefully faster than checking each word individually.
subkey = set(letters)
for key, words in words_by_letters.iteritems():
if key.issuperset(subkey):
matches.extend(words)
If you want to keep track of letter repeats, you can do something similar by building a key from collections.Counter.

switching letters to different letters inside a string (Caesar cipher)

I've got some homework involving Caesar cipher, and I got stuck here:
I need to write a function which gets a text (as a String) and a dictionary. The dictionary keys are the English ABC, and its values are other letters from the ABC.
My goal is to go over the text, and wherever there is a letter (only letters!)
change it to the value belongs to the specific letter in the dictionary.
edit: my function should return the deciphered text as a string.
You're looking for the translate method:
>>> u"abc".translate({ord('a'): u'x', ord('b'): u'y', ord('c'): u'z'})
'xyz'
Look at maketrans if you're using bytestrings or if your Python is older than 2.7.
A bit of pseudocode (language agnostic). You should be able to take it from here.
cipher = array
caesar_mask = [ A: G, ... , Z: F ]
for each letter_index in text
cipher_letter = caesar_mask[text[letter_index]]
cipher[] = cipher_letter
end
First question is if you have to do it in place.
Then I would look into these things:
list comprehension
map()
how to iterate through letters in string
how to join a sequence of letters to create string
how to replace characters in string
Not in any specific order and not necesarily all inclusive.

Categories

Resources