going through a dictionary and printing its values in sequence - python

def display_hand(hand):
for letter in hand.keys():
for j in range(hand[letter]):
print letter,
Will return something like: b e h q u w x. This is the desired output.
How can I modify this code to get the output only when the function has finished its loops?
Something like below code causes me problems as I can't get rid of dictionary elements like commas and single quotes when printing the output:
def display_hand(hand):
dispHand = []
for letter in hand.keys():
for j in range(hand[letter]):
##code##
print dispHand
UPDATE
John's answer is very elegant i find. Allow me however to expand o Kugel's response:
Kugel's approach answered my question. However i kept running into an additional issue: the function would always return None as well as the output. Reason: Whenever you don't explicitly return a value from a function in Python, None is implicitly returned. I couldn't find a way to explicitly return the hand. In Kugel's approach i got closer but the hand is still buried in a FOR loop.

You can do this in one line by combining a couple of list comprehensions:
print ' '.join(letter for letter, count in hand.iteritems() for i in range(count))
Let's break that down piece by piece. I'll use a sample dictionary that has a couple of counts greater than 1, to show the repetition part working.
>>> hand
{'h': 3, 'b': 1, 'e': 2}
Get the letters and counts in a form that we can iterate over.
>>> list(hand.iteritems())
[('h', 3), ('b', 1), ('e', 2)]
Now just the letters.
>>> [letter for letter, count in hand.iteritems()]
['h', 'b', 'e']
Repeat each letter count times.
>>> [letter for letter, count in hand.iteritems() for i in range(count)]
['h', 'h', 'h', 'b', 'e', 'e']
Use str.join to join them into one string.
>>> ' '.join(letter for letter, count in hand.iteritems() for i in range(count))
'h h h b e e'

Your ##code perhaps?
dispHand.append(letter)
Update:
To print your list then:
for item in dispHand:
print item,

another option without nested loop
"".join((x+' ') * y for x, y in hand.iteritems()).strip()

Use
" ".join(sequence)
to print a sequence without commas and the enclosing brackets.
If you have integers or other stuff in the sequence
" ".join(str(x) for x in sequence)

Related

list comprehension without if but with else

My question aims to use the else condition of a for-loop in a list comprehension.
example:
empty_list = []
def example_func(text):
for a in text.split():
for b in a.split(","):
empty_list.append(b)
else:
empty_list.append(" ")
I would like to make it cleaner by using a list comprehension with both for-loops.
But how can I do this by including an escape-clause for one of the loops (in this case the 2nd).
I know I can use if with and without else in a list comprehension. But how about using else without an if statement.
Is there a way, so the interpreter will understand it as escape-clause of a for loop?
Any help is much appreciated!
EDIT:
Thanks for the answers! In fact im trying to translate morse code.
The input is a string, containing morse codes.
Each word is separated by 3 spaces. Each letter of each word is separated by 1 space.
def decoder(code):
str_list = []
for i in code.split(" "):
for e in i.split():
str_list.append(morse_code_dic[e])
else:
str_list.append(" ")
return "".join(str_list[:-1]).capitalize()
print(decoder(".. - .-- .- ... .- --. --- --- -.. -.. .- -.--"))
I want to break down the whole sentence into words, then translate each word.
After the inner loop (translation of one word) is finished, it will launch its escape-clause else, adding a space, so that the structure of the whole sentence will be preserved. That way, the 3 Spaces will be translated to one space.
As noted in comments, that else does not really make all that much sense, since the purpose of an else after a for loop is actually to hold code for conditional execution if the loop terminates normally (i.e. not via break), which your loop always does, thus it is always executed.
So this is not really an answer to the question how to do that in a list comprehension, but more of an alternative. Instead of adding spaces after all words, then removing the last space and joining everything together, you could just use two nested join generator expressions, one for the sentence and one for the words:
def decoder(code):
return " ".join("".join(morse_code_dic[e] for e in i.split())
for i in code.split(" ")).capitalize()
As mentioned in the comments, the else clause in your particular example is pointless because it always runs. Let's contrive an example that would let us investigate the possibility of simulating a break and else.
Take the following string:
s = 'a,b,c b,c,d c,d,e, d,e,f'
Let's say you wanted to split the string by spaces and commas as before, but you only wanted to preserve the elements of the inner split up to the first occurrence of c:
out = []
for i in s.split():
for e in i.split(','):
if e == 'c':
break
out.append(e)
else:
out.append('-')
The break can be simulated using the arcane two-arg form of iter, which accepts a callable and a termination value:
>>> x = list('abcd')
>>> list(iter(iter(x).__next__, 'c'))
['a', 'b']
You can implement the else by chaining the inner iterable with ['-'].
>>> from itertools import chain
>>> x = list('abcd')
>>> list(iter(chain(x, ['-'])
.__next__, 'c'))
['a', 'b']
>>> y = list('def')
>>> list(iter(chain(y, ['-'])
.__next__, 'c'))
['d', 'e', 'f', '-']
Notice that the placement of chain is crucial here. If you were to chain the dash to the outer iterator, it would always be appended, not only when c is not encountered:
>>> list(chain(iter(iter(x).__next__, 'c'), ['-']))
['a', 'b', '-']
You can now simulate the entire nested loop with a single expression:
from itertools import chain
out = [e for i in s.split() for e in iter(chain(i.split(','), ['-']).__next__, 'c')]

How to split a list based on whether the elements were next to each other in the list they came from?

I'm going through Problem 3 of the MIT lead python course, and I have an admittedly long drawn out script that feels like it's getting close. I need to print the longest substring of s in which the letters occur in alphabetical order. I'm able to pull out any characters that are in alphabetical order with regards to the character next to it. What I need to see is:
Input : 'aezcbobobegghakl'
needed output: 'beggh'
my output: ['a', 'e', 'b', 'b', 'b', 'e', 'g', 'g', 'a', 'k']
My code:
s = 'aezcbobobegghakl'
a = 'abcdefghijklmnopqrstuvwxyz'
len_a = len(a)
len_s = len(s)
number_list = []
letter_list = []
for i in range(len(s)):
n = 0
letter = s[i+n]
if letter in a:
number_list.append(a.index(letter))
n += 1
print(number_list)
for i in number_list:
letter_list.append(a[i])
print(letter_list)
index_list = []
for i in range(len(letter_list)):
index_list.append(i)
print(index_list)
first_check = []
for i in range(len(letter_list)-1):
while number_list[i] <= number_list[i+1]:
print(letter_list[i])
first_check.append(letter_list[i])
break
print(first_check)
I know after looking that there are much shorter and completely different ways to solve the problem, but for the sake of my understanding, is it even possible to finish this code to get the output I'm looking for? Or is this just a lost cause rabbit hole I've dug?
I would build a generator to output all the runs of characters such that l[i] >= l[i-1]. Then find the longest of those runs. Something like
def runs(l):
it = iter(l)
try:
run = [next(it)]
except StopIteration:
return
for i in it:
if i >= run[-1]:
run.append(i)
else:
yield run
run = [i]
yield run
def longest_increasing(l):
return ''.join(max(runs(l), key=len))
Edit: Notes on your code
for i in range(len(s)):
n = 0
letter = s[i+n]
if letter in a:
number_list.append(a.index(letter))
n += 1
is getting the "number value" for each letter. You can use the ord function to simplify this
number_list = [ord(c) - 97 for c in s if c.islower()]
You never use index_list, and you never should. Look into the enumerate function.
first_check = []
for i in range(len(letter_list)-1):
while number_list[i] <= number_list[i+1]:
print(letter_list[i])
first_check.append(letter_list[i])
break
this part doesn't make a ton of sense. You break out of the while loop every time, so it's basically an if. You have no way of keeping track of more than one run. You have no mechanism here for comparing runs of characters against one another. I think you might be trying to do something like
max_run = []
for i in range(len(letter_list)-1):
run = []
for j in range(i, len(letter_list)):
run.append(letter_list[j])
if letter_list[j] > letter_list[j+1]:
break
if len(run) > len(max_run):
max_run = run
(Disclaimer: I'm pretty sure the above is off by one but it should be illustrative). The above can be improved in a lot of ways. Note that it loops over the last character as many as len(s) times, making it a n**2 solution. Also, I'm not sure why you need number_list, as strings can be compared directly.
What about a simple recursive approach :
data = 'ezcbobobegghakl'
words=list(data)
string_s=list(map(chr,range(97,123)))
final_=[]
def ok(list_1,list_2):
if not list_1:
return 0
else:
first = list_1[0]
chunks = list_2[list_2.index(first):]
track = []
for j, i in enumerate(list_1):
if i in chunks:
track.append(i)
chunks=list_2[list_2.index(i):]
else:
final_.append(track)
return ok(list_1[j:],list_2)
final_.append(track)
print(ok(words,string_s))
print(max(final_,key=lambda x:len(x)))
output:
['b', 'e', 'g', 'g', 'h']
You can find a list of all substrings of the input string, and then find all the strings that are sorted alphabetically. To determine of a letter is sorted alphabetically, sorted the original string by position in the alphabet, and then see if the final string equals the original string:
from string import ascii_lowercase as l
s = 'aezcbobobegghakl'
substrings = set(filter(lambda x:x, [s[i:b] for i in range(len(s)) for b in range(len(s))]))
final_substring = max([i for i in substrings if i == ''.join(sorted(list(i), key=lambda x:l.index(x)))], key=len)
Output:
'beggh'
This is one way of getting the job done:
s = 'aezcbobobegghakl'
l = list(s)
run = []
allrun = []
element = 'a'
for e in l:
if e >= element:
run.append(e)
element = e
else:
allrun.append(run)
run = [e]
element = e
lengths = [len(e) for e in allrun]
result = ''.join(allrun[lengths.index(max(lengths))])
"run" is basically an uninterrupted run; it keeps growing as you add elements bigger than what is previously seen ("b" is bigger than "a", just string comparison), and resets else.
"allrun" contains all "run"s, which looks like this:
[['a', 'e', 'z'], ['c'], ['b', 'o'], ['b', 'o'], ['b', 'e', 'g', 'g', 'h']]
"result" finally picks the longest "run" in "allrun", and merges it into one string.
Regarding your code:
It is very very inefficient, I would not proceed with it. I would adopt one of the posted solutions.
Your number_list can be written as [a.index(_) for _ in s], one liner.
Your letter_list is actually just list(s), and you are using a loop for that!
Your index_list, what does it even do? It is equivalent to range(len(letter_list)), so what are you aiming with the append in the loop?
Finally, the way you write loops reminds me of matlab. You can just iterate on the elements of a list, no need to iterate on index and fetch the corresponding element in list.

How to create a function that finds the index of a word in a list?

I'm trying to extend my hangman game so that your able to select the amount of letters you want your word to be. I'll do this by using the selection method (e.g. if,else) however after making the function i come across an error so i threw away the whole function after finding where the error came from and started working on solving the error. Basically I'm trying to use index() function to locate where the element is but for some reason it doesn't work it always outputs 0.
def wordLength(word):
r = word
num=word.index(r)
print(num)
I think what you are trying to achieve is this (since you mentioned you used .split() in the comments on your question:
sentence = "this is a sentence"
sentence_split = sentence.split()
def wordLength(word, sentence):
try:
index = sentence.index(word)
print(index)
except:
print("word not in sentence")
wordLength("a", sentence_split)
Results in '3', which is the position of 'a' within your sentence.
EDIT
Or, if you want the index number of each letter within each word..
sentence = "this is a sentence"
sentence_split = sentence.split()
letter_index = []
def index_letters():
for i in sentence_split:
# I results in "this" (1st loop), "is" (2nd loop), etc.
for x in range(len(i)):
# loops over every word, and then counts every letter. So within the i='this' loop this will result in four loops (since "this" has 4 letters) in which x = 0 (first loop), 1 (second loop), etc.
letter = i[x]
index = i.index(letter)
letter_index.append([index, letter])
return letter_index
print(index_letters())
Results in: [[0, 't'], [1, 'h'], [2, 'i'], [3, 's'], [0, 'i'], [1, 's'], [0, 'a'], [0, 's'], [1, 'e'], [2, 'n'], [3, 't'], [1, 'e'], [2, 'n'], [6, 'c'], [1, 'e']]
If I understand what you are asking then you should be able to take the word chosen for the hangman game and create a list of every letter in the word then finding the index() of the letter in the list.
Without the question being more clear it seams like you are asking for the index of a letter in a word. What I have for that is below.
something like this should help:
hm_word = [] #the list to be created for the given word
def wordlist(hangman_word): #Creates a list of each letter of the word
for letter in hangman_word:
hm_word.append(letter)
wordlist("bacon")# calls the function and puts in the word chosen
print (hm_word) #just to show the list has been created
print (hm_word.index("o")+1) #This prints the index of the letter 'o' however you can use a user input here.
#NOTE: I used +1 in the print for the index to show the index number starting from 1 not 0
you can use this as a starting point to get the index of the letter being guessed by taking the user input and placing it in the index().
I only used "o" as an example of how it works.
def find_word(word, word_list):
try:
return word_list.index(word)
except ValueError:
return None # or what ever you need
>>> word_list = ['bubble', 'lizzard', 'fish']
>>> find_word('lizzard', word_list)
1
>>> print(find_word('blizzard', word_list))
None
It's returning 0, because you are running index on the input variable for itself. You will need to index on a segment of the input variable. A slight modification of your example, that will return something other than 0:
def wordLength(word):
r = 'r'
num=word.index(r)
print(num)
This should print the number 2.
"wordLength" is a bit of a misnomer here for your function. If you want the length of a word, you can just do:
len(word)
def findChar(word, char):
# don't go any further if they aren't strings
assert isinstance(word, basestring)
assert isinstance(char, basestring)
# return the character you are looking for,
# in human friendly form (1 more than the python index)
return word.lower().index(char.lower()) + 1
to use:
>>>print findChar('heat', 'a')
3 # a is in the third position.
This does not address a single character being in a word many times, obviously.

How to print the first letter of each word in a sentence?

I got this question in a quiz last week, a lot of people got it wrong, so I am pretty sure it will be on our midterm:
Write a function that takes as a parameter a list of strings and
returns a list containing the first letter of each of the strings.
That is, if the input parameter is ["Daniel","Nashyl","Orla",
"Simone","Zakaria"], your function should return ['D', 'N', 'O', 'S',
'Z']. The file you submit should include a main() function that
demonstrates that your function works.
I know you can use this [#:#] to print any letters of a word or sentence.
>>> `x = "Java, Python, Ruby"`
>>> `x[:13]`
'Java, Python,'
>>> `x[:-1]`
'Java, Python, Rub'
>>> `x[:1]`
'J'
But I get confused when it comes to printing the first letter of a bunch of words. I also think that the ".split" function is needed here. I am using python 3.3.3
def first_letters(lst):
return [s[:1] for s in lst]
def main():
lst = ["Daniel","Nashyl","Orla", "Simone","Zakaria"]
assert first_letters(lst) == ['D', 'N', 'O', 'S', 'Z']
if __name__=="__main__":
main()
str.split takes a string and breaks it into a list of strings. Your input is already a list of strings, therefore you do not need .split.
"mystring"[:1] gets the first character of the string (or "" if the string is "" to begin with). Apply this to each string in the input list, and return the result.
You can do this with a list comprehension. You'll definitely want to read about them! Here's a minimal example that does what you're looking for:
>>> L = ["Daniel","Nashyl","Orla", "Simone","Zakaria"]
>>> [item[0] for item in L]
['D', 'N', 'O', 'S', 'Z']
This loops through each name in your list and creates a new list from the first letter of each item in the original list. For example, "Daniel"[0] == 'D'. No .split is needed.
List comprehensions are cool, and you should learn to use them indeed, but let me explain a bit what's going on here, since in your question you said you're confused how to do it with a bunch of strings.
So, you have a list of strings. Lists are an iterable collection, which means we can iterate through it using, for example, a for loop:
words = ["Daniel","Nashyl","Orla", "Simone","Zakaria"]
for word in words:
print word[:1]
I'm sure you were taught about loops like this in class. Now, instead of printing the first letter, let's construct a new list that contains those letters:
result = []
for word in words:
result.append(word[:1])
Here I created a new list, then for every word, I appended the starting letter of that word to the new list. A list comprehension does the same thing, with a more obscure syntax, more elegance, and a bit more efficiency:
result = [word[:1] for word in words]
This is the gist of it.

Manipulating counter information - Python 2.7

I'm fairly new to Python and I have this program that I was tinkering with. It's supposed to get a string from input and display which character is the most frequent.
stringToData = raw_input("Please enter your string: ")
# imports collections class
import collections
# gets the data needed from the collection
letter, count = collections.Counter(stringToData).most_common(1)[0]
# prints the results
print "The most frequent character is %s, which occurred %d times." % (
letter, count)
However, if the string has 1 of each character, it only displays one letter and says it's the most frequent character. I thought about changing the number in the parenthesis in most_common(number), but I didn't want more to display how many times the other letters every time.
Thank you to all that help!
As I explained in the comment:
You can leave off the parameter to most_common to get a list of all characters, ordered from most common to least common. Then just loop through that result and collect the characters as long as the counter value is still the same. That way you get all characters that are most common.
Counter.most_common(n) returns the n most common elements from the counter. Or in case where n is not specified, it will return all elements from the counter, ordered by the count.
>>> collections.Counter('abcdab').most_common()
[('a', 2), ('b', 2), ('c', 1), ('d', 1)]
You can use this behavior to simply loop through all elements, ordered by their count. As long as the count is the same as of the first element in the output, you know that the element still ocurred in the same quantity in the string.
>>> c = collections.Counter('abcdefgabc')
>>> maxCount = c.most_common(1)[0][1]
>>> elements = []
>>> for element, count in c.most_common():
if count != maxCount:
break
elements.append(element)
>>> elements
['a', 'c', 'b']
>>> [e for e, c in c.most_common() if c == maxCount]
['a', 'c', 'b']

Categories