Quicker solution to the problem with loops and dictionaries - python

I have solved one issue recentely and I know for sure that there must be quicker solution to the problem. I am just learning and I want to write efficient code so it would be helpful if you could suggest me anything.
I want this output (which I got):
Jozefína likes to go to here: Amsterdam ,Liverpool ,London.
Marek likes to go to here: Košice.
Jozef likes to go to here: Lisabon ,Vancouver.
PS: names of people and cities are totaly random so you can change it
And my code for this problem is:
favourite_places = {
"Jozefína": ["Amsterdam", "Liverpool", "London"],
"Marek": ["Košice"],
"Jozef": ["Lisabon", "Vancouver"]
}
for name in favourite_places:
line = ""
for place in favourite_places[name]:
if len(favourite_places[name]) == 1:
line = place
else:
line += place + " ,"
if line[-1] == ",":
line = line[0:-2]
print(f"{name} likes to go to here: {line}.")
In the end I want to have one sentence with name of the person and places that he or she likes to go to (devided by the comma).
I hope I described the problem correctely. I just want to optimalize my code. I will be grateful for any suggestions.

What you are missing is a join and items functions.
join joins all items of iterable with specified delimiter. If there is only 1 item, no delimiter will be added. It is generally considered a bad practise to concatenate strings with + or +=. Strings are immutable and thus each concatenation produces a new string, that has to have memory allocated for it. To avoid this, you could first collect all parts of strings into list (which you already have as an input) and then join them.
items() returns tuples of (key, value) of dictionary.
favourite_places = {
"Jozefína": ["Amsterdam", "Liverpool", "London"],
"Marek": ["Košice"],
"Jozef": ["Lisabon", "Vancouver"]
}
for name, places in favourite_places.items():
print(f"{name} likes to go to here: {', '.join(places)}.")

The simplest way I can think of to accomplish this is like this:
for name, places in favourite_places.items():
print(f'{name} likes to go here: {", ".join(places)}.')
The .items() returns a list of (key, value) tuples from the dictionary, which are deconstructed into name and places, then the ", ".join(places) will return a string that separates each element in the list of places with a comma, and will automatically leave it out if there is only a single element.

Try using replace
for name, places in favourite_places.items():
print(name + " likes to go to here: " + str(places).replace('[', '').replace("'", "").replace(']', ''))

here is some improvement:
favourite_places = {
"Jozefína": ["Amsterdam", "Liverpool", "London"],
"Marek": ["Košice"],
"Jozef": ["Lisabon", "Vancouver"]
}
for name, place in favourite_places.items():
line = ', '.join(place)
print(f'{name} likes to go to here: {line}')

Related

Unexpected print output with None's when using a list comprehension

I tried to rewrite the first block of code with a list comprehension, but it prints a strange list with two None elements.
pizza = { 'crust': 'thick', 'toppings': ['mushrooms', 'extra cheese'], }
# summarizing the order:
print("You ordered a " + pizza['crust'] + "-topping" + " with:" )
for topping in pizza['toppings']:
print("\t" + topping.title())
Second block with the list comprehension:
pizza = { 'crust': 'thick', 'toppings': ['mushrooms', 'extra cheese'], }
# summarizing the order:
print("You ordered a " + pizza['crust'] + "-topping" + " with:" )
[print("\t" + topping.title()) for topping in pizza['toppings']]
Output of the second block:
You ordered a thick-topping with:
Mushrooms
Extra Cheese
[None, None]
I experimented by assigning the list comprehension to a variable, after which the None list disappeared. But I don't understand why.
The problem is that you are including the print statement in the for loop, which is non-serializable as a Python function and thus cannot be displayed. What you really want to do is print the list of toppings, not the list of print statements along with the toppings themselves. Try refactoring your code to look like this instead:
print(["\t" + topping.title()) for topping in pizza['toppings']])
This will, however, print a list instead of a series of strings, which differs from your original code. If you want to do that with list comprehension, then you'll have to write two lines of code:
toppings = ["\t" + topping.title() for topping in pizza['toppings']]
for topping in toppings: print(topping)
In reality, this is much better accomplished using the first method you described and I wouldn't recommend list comprehension unless you're planning to use the toppings list later on. Generally, list comprehension is very useful for quickly creating lists, but not for executing series of statements, for which a for loop does a much better job.
Store the Toppings in a list variable and then iterate over the list to print the values.
Although honestly in this case I would use the first block of code raised.

Case insensitive Full Name dictionary search

I am creating a dictionary with "Full Name": "Birthday" for numerous people as an exercise.
The program should ask
"Who's birthday do you want to look up?"
I will input a name, say "Benjamin Franklin"
And it will return his birthday: 1706/01/17.
Alright, the problem I am encountering is name capitalization.
How can I input "benjamin franklin" and still find "Benjamin Franklin" in my dictionary? I am familiar with .lower() and .upper() functions, however I am not able to implement them correctly, is that the right way to approach this problem?
Here is what I have
bday_dict = {"Person1": "YYYY/MM/DD1",
"Person2": "YYYY/MM/DD2",
"Benjamin Franklin": "1706/01/17"}
def get_name(dict_name):
name = input("Who's birthday do you want to look up? > ")
return name
def find_bday(name):
print(bday_dict[name])
find_bday(get_name(bday_dict))
The best way to do this is to keep the keys in your dictionary lowercase. If you can't do that for whatever reason, have a dictionary from lowercase to the real key, and then keep the original dictionary.
Otherwise, Kraigolas's solution works well, but it is O(N) whereas hashmaps are supposed to be constant-time, and thus for really large dictionaries the other answer will not scale.
So, when you are setting your keys, do bday_dict[name.lower()] = value and then you can query by bday_dict[input.lower()].
Alternatively:
bday_dict = {"John": 1}
name_dict = {"john": "John"}
def access(x):
return bday_dict[name_dict[x.lower()]]
Probably the most straight forward way I can think of to solve this is the following:
def get_birthday(name):
global bday_dict
for person, bday in bday_dict.items():
if name.lower() == person.lower():
return bday
return "This person is not in bday_dict"
Here, you just iterate through the entire dictionary using the person's name paired with their birthday, and if we don't find them, just return a message saying we don't have their birthday.
If you know that all names will capitalize the first letter of each word, you can just use:
name = ' '.join([word.capitalize() for word in name.split()])
then you can just search for that. This is not always the case. For example, for "Leonardo da Vinci" this will not work, so the original answer is probably the most reliable way to do this.
One final way to do this would be to just store the names as lowercase from the beginning in your dictionary, but this might not be practical when you want to draw a name from the dictionary as well.
Depending what your exercise allows, I would put the names in the dictionary as all lowercase or uppercase. So:
bday_dict = {"person1": "YYYY/MM/DD1",
"person2": "YYYY/MM/DD2",
"benjamin franklin": "1706/01/17"}
And then look up the entered name in the dictionary like this:
def find_bday(name):
print(bday_dict[name.lower()])
You may also want to do a check that the name is in the dictionary beforehand to avoid an error:
def find_bday(name):
bday = bday_dict.get(name.lower(), None)
if bday:
print(bday)
else:
print("No result for {}.".format(name))

Extracting a string from a tuple in Python

I am trying to make a dictionary of sorts using tuples. The idea is to store a word along with its description in a tuple. The tuple then goes into a list. After that, I'm supposed to be able to look up the meaning of a word in the dictionary by typing the word I want a description of.
My problem is to extract only the description part of the tuple from the list and print only that based on what word the user wants to look up. I do have a function that seems to work for making the tuples and storing them in the list but I think that function also is wrong.
This is as far as I have been able to come:
def tuples():
dictionary = []
while True:
print("\n--- Menu for dictionary ---\n Choose 1 to insert a word\n Choose 2 to lookup a word\n Choose 3 to quit\n")
answer = input("Write your answer here: ")
if answer == "1":
insert(dictionary)
elif answer == "2":
lookup(dictionary)
elif answer == "3":
break
else:
print("\nTry again!\n")
def insert(dictionary):
word = input("What word would you like to add: ")
des = input("Type a description of that word: ")
info = (word, des)
dictionary.append(info)
def lookup(dictionary):
word = input("What word do you want to lookup: ")
place = dictionary.index(word)
print("\nDescription of", word,":", dictionary[place], "\n")
Similar to the other answer, this example loops through the list of tuples examining the word part of the tuple to get to the description part. It differs in a number of respects, but the most important difference is that it uses tuple unpacking versus subscripting to get the contents of the tuple. To illustrate the key concepts I left out the user input part.
Note: If the list of tuples was long enough, you would want to consider sorting it and using something like the the bisect standard library to more efficiently search it and update it.
Example:
dictionary = [("cat", "Four legs, scratches."), ("dog", "Four legs, wags."), ("gerbil", "Four legs, kangaroo-like.")]
def find_description(dictionary, search_term):
# Note use of automatic tuple "unpacking"
for word, description in dictionary:
if word == search_term:
print(f"Description of {word}: {description}")
break
else: # no-break
print(f"Could not find {search_term} in dictionary.")
find_description(dictionary, "gerbil")
find_description(dictionary, "hamster")
Output:
Description of gerbil: Four legs, kangaroo-like.
Could not find hamster in dictionary.
I think you can achieve what you are trying to do by modifying your lookup function
to use a generator expression to search the dictionary list for the query. I got your example to work with the following modification to lookup():
def lookup(dictionary):
word = input("What word do you want to lookup: ")
place = next((i for i, v in enumerate(dictionary) if v[0] == word), None)
print("\nDescription of", word,":", dictionary[place][1], "\n")
If you are concerned with runtime I would recommend abstracting out the (word, des) tuple out to a class that could be hashable such that you can use a dictionary as your dictionary, utilizing the faster lookup. This also would solve the issue of duplicate entries.

Getting data outside of a list in python

I have list with one item in it, then I try to dismantle, & rebuild it.
Not really sure if it is the 'right' way, but for now it will do.
I tried using replace \ substitute, other means of manipulating the list, but it didn't go too far, so this is what I came up with:
This is the list I get : alias_account = ['account-12345']
I then use this code to remove the [' in the front , and '] from the back.
NAME = ('%s' % alias_account).split(',')
for x in NAME:
key = x.split("-")[0]
value = x.split("-")[1]
alias_account = value[:-2]
alias_account1 = key[2:]
alias_account = ('%s-%s') % (alias_account1, alias_account)
This works beautifully when running print alias_account.
The problem starts when I have a list that have ['acc-ount-12345'] or ['account']
So my question is, how to include all of the possibilities?
Should I use try\except with other split options?
or is there more fancy split options ?
To access a single list element, you can index its position in square brackets:
alias_account[0]
To hide the quotes marking the result as a string, you can use print():
print(alias_account[0])

How can I call all items in a list item?

So in django, lets say you create a list through .append like so:
group = []
people = humans.objects.all()
for X in people:
X.update(name = Bob)
group.append(X.idnum)
If you wanted to get some of the items in the group list without it displaying like this " u'23' ",
you would have to call group[0] or any othe number to find the one youre looking for. My question is how can I get all of them at once without the u''. So if i have three peope in the group, I want their idnums to come out as 232528 instead of " u'23' u'25' u'28' " without me haveing to do group[0][1][2] since I want always know how many are in the list.
You want the str.join method. https://docs.python.org/2/library/stdtypes.html#str.join
"".join(group)
or if you want a number instead of a string
int("".join(group))
I would use a iteration and then add the string together:
list = [...]
result = ''
for item in list:
result += str(item)
The result would be '232528' if the items in the list are u'23' u'25' u'28'

Categories