I have a dictionary with the last and first names of the authors being the key, and the book, quantity, and price being the values. I want to print them out sorted in alphabetical order by the author name, and then by the book name.
The author is: Dickens, Charles
The title is: Hard Times
The qty is: 7
The price is: 27.00
----
The author is: Shakespeare, William
The title is: Macbeth
The qty is: 3
The price is: 7.99
----
The title is: Romeo And Juliet
The qty is: 5
The price is: 5.99
I'm very new to dictionaries and can't understand how you can sort a dictionary. My code so far is this:
def displayInventory(theInventory):
theInventory = readDatabase(theInventory)
for key in theInventory:
for num in theInventory[key]:
print("The author is", ' '.join(str(n) for n in key))
print(' '.join(str(n) for n in num), '\n')
The dictionary, when printed, from which I read this looks like this:
defaultdict(<class 'list'>, {('Shakespeare', 'William'): [['Rome And Juliet', '5', '5.99'], ['Macbeth', '3', '7.99']], ('Dickens', 'Charles'): [['Hard Times', '7', '27.00']]})
fwiw, camelCase is very uncommon in Python; almost everything is written in snake_case. :)
I would do this:
for names, books in sorted(inventory.items()):
for title, qty, price in sorted(books):
print("The author is {0}".format(", ".join(names)))
print(
"The book is {0}, and I've got {1} of them for {2} each"
.format(title, qty, price))
print()
Ignoring for the moment that not everyone has a first and last name...
There are some minor tricks involved here.
First, inventory.items() produces a list of key, value tuples. I can then sort that directly, because tuples sort element-wise — that is, (1, "z") sorts before (2, "a"). So Python will compare the keys first, and the keys are tuples themselves, so it'll compare last names and then first names. Exactly what you want.
I can likewise sort books directly because I actually want to sort by title, and the title is the first thing in each structure.
I can .join the names tuple directly, because I already know everything in it should be a string, and something is wrong if that's not the case.
Then I use .format() everywhere because str() is a bit ugly.
The key is to use sorted() to sort the dictionary by its keys, but then use sort() on the dictionaries values. This is necessary because your values are actually a list of lists and it seems you want only to sort them by the first value in each sub-list.
theInventory = {('Shakespeare', 'William'): [['Rome And Juliet', '5', '5.99'], ['Macbeth', '3', '7.99']], ('Dickens', 'Charles'): [['Hard Times', '7', '27.00']]}
for Author in sorted(theInventory.keys()):
Author_Last_First = Author[0]+", "+Author[1]
Titles = theInventory[Author]
Titles.sort(key=lambda x: x[0])
for Title in Titles:
print("Author: "+str(Author_Last_First))
print("Title: "+str(Title[0]))
print("Qty: "+str(Title[1]))
print("Price: "+str(Title[2]))
print("\n")
Is that what you had in mind? You can of course always put this in a function to make calling it easier.
Related
I'm new to Python but have worked in R for a while but am mostly used to working with data frames. I web-scraped the following for which the output is four lists included below: but with values taken out to create a minimal reproducible example.
I am trying to put these into a list of dictionaries such that the data is set up in the following way.
Each dictionary, rankings[i], should have these keys:
rankings[i]['name']: The name of the restaurant, a string.
rankings[i]['stars']: The star rating, as a string, e.g., '4.5', '4.0'
rankings[i]['numrevs']: The number of reviews, as an integer.
rankings[i]['price']: The price range, as dollar signs, e.g., '$', '$$', '$$$', or '$$$$'.
I get so confused by dictionaries within lists and sequences of sequences in general, so if you have any great resources, please link them here! I've been reading through Think Python.
This is what I have, but it ends up returning one dictionary with lists of values, which is not what I need.
Thanks much!
def Convert(tup, di):
for w, x,y,z in tup:
di.setdefault(w, []).extend((w,x,y,z))
return di
yelp = list(zip(name, stars, numrevs, price))
dictionary = {}
output = Convert(yelp, dictionary)
#data
name = ['Mary Mac’s Tea Room', 'Busy Bee Cafe', 'Richards’ Southern Fried']
stars = ['4.0 ', '4.0 ', '4.0']
numrevs = ['80', '49', '549']
price = ['$$', '$$', '$$']
Update:
This will give me a dictionary of a single restaurant:
def Convert(tup, di):
dictionary = {}
for name, stars, numrevs, price in tup:
yelp = list(zip(name, stars, numrevs, price))
name, stars, numrevs, price = tup[0]
entry = {"name" : name,
"stars" : stars,
"numrevs": numrevs,
"print" : price}
return entry
output = Convert(yelp, dictionary)
output
This is my attempt to iterate over the all restaurants and add them to a list. It looks like I am only getting the final restaurant and everything else is being written over. Perhaps I need to do something like
def Convert(tup, di):
dictionary = {}
for name, stars, numrevs, price in tup:
yelp = list(zip(name, stars, numrevs, price))
for i in name, stars, numrevs, price:
l = []
entry = {"name" : name,
"stars" : stars,
"numrevs": numrevs,
"price" : price}
l.append(entry)
return l
output = Convert(yelp, dictionary)
output
There is nothing in your function that attempts to build a list of dicts in the format you want. You make each value with the extend method. This adds to a list; it does not contrsuct a dict.
Start from the inside: build a single dict in the format you want, from only the first set of elements. Also, use meaningful variable names -- do you expect everyone else to learn your one-letter mappings, so you can save a few seconds of typing?
name, stars, numrevs, price = tup[0]
entry = {"name" : name,
"stars" : stars,
"numrevs": numrevs,
"print" : price}
That is how you make a single dict for your list of dicts.
That should get you started. Now you need to (1) learn to add entry to a list; (2) iterate through all of the quadruples in tup (another meaningless name to replace) so that you end up adding each entry to the final list, which you return.
Update for second question:
It keeps overwriting because you explicitly tell it to do so. Every time through the loop, you reset l to an empty list. Stop that! There are plenty of places that show you how to accumulate iteration results in a list. Start by lifting the initialization to before the loop.
l = []
for ...
I am creating a Student Grading program: it starts in input mode and asks for name, age, science grade, maths grade, and English grade.
Then there are multiple possible actions: add students, add school-classes, print every added student, and - print lowest-to-highest results in the different subjects. This last action is the one I am having problems with.
What I'm failing in is when the user inputs ‘4’ in the main menu, then user is asked if he/she wants to display the students maths, physics, or English results. When the user specifies what they want to display, something like the following will be produced:
Jill: 3
Marvin: 4
Jack: 6
Raf: 80
I know how to sort through the numbers with the sort() function but how do I make ‘jill’ go in the same place as ‘3’ on a list.
This is so I can do something like this:
for p in range(len(allStudents)):
print(allStudents[p].printName(), ":" , mathsAll[p] , "/100")
I know that I could use a dictionary for this but then let’s say I input 2 users called “Jill”, then it will overwrite the first one.
full code can be found from: http://pastebin.com/R2uGAQVg
Any help would be greatly appreciated!!
You need to pair the name with the value. If you don't want to use a dictionary for some reason, then use tuples.
data = [('Jill', 3), ('Marvin', 4), ('Jack', 6), ('Raf', 80)]
for datum in data:
name, score = data
# do whatever you like....
Or use a list of dictionaries
data = [{'name': 'Jill', 'score': '3'}, ... ]
for d in data:
print(d['name'] + ": " + d['score'])
Or use namedtuples.
from collections import namedtuple
Student = namedtuple('Student', ['name', 'score'])
data = [Student(name="Jill", score=3), ... ]
for student in data:
print(data.name + ": " + data.score)
# or print("{student.name}: {student.score}".format(student=student))
Or any of a number of other better ways to handle your data than two unrelated lists!!
You can store your students in a class:
class Student:
def __init__(self, name, math_grade):
self.name = name
self.math_grade = math_grade
then if you have a list of students student_list, you can sort the list by attribute:
student_list = [Student("Steve", 3), Student("Paul", 4), Student("Jack, 5")]
student_list.sort(key=lambda x: x.math_grade, reverse=True)
print(student_list)
Then if the students have more attributes (age, name, science grade, english grade, etc), is easy to sort them by changing the lambda.
If your application will hold information about students between runs, you should consider other kind of containers like an embedded database (such as SQL light).
I'm not sure what the best way to write a title for this question is, so sorry for the somewhat poor title. Instead, I'll just write an example.
What I want to do is take the items from a dictionary and place them in a list, (which I have done with the following code).
for key,val in teamDict.items(): teamlist.append((key,val))
Now this list contains info on teams in the NFL, and is stored in the form:
[('DAL', [(2011, 'Chauncey Washington', 47.29080932784636, 1, -1, 0, 0), (2011, 'DeMarco Murray', 90.58014654220617, 164, 897, 2, 1)...]]
And so on, with every team being represented.
Now what I want is for the output to look something like this
Roy Helu 2011 81.33
Ryan Torain 2011 78.16
Tim Hightower 2011 84.20
Andre Brown 2010 50.03
Brandon Banks 2010 69.24
Clinton Portis 2010 108.35
Only for every player from 2011 to be printed after every player from 2010 has been printed. Everything is formatted correctly and printing out correctly, the only trouble I'm running into is not being able to get the years sorted properly. Also here is the code that sorts through the list:
for item in teamlist:
for sub_item in item:
teamName = item[0]
if user_input == teamName:
for sub_sub_item in sub_item:
print(" {:<25} {:^6} {:>8.2f}".format(sub_sub_item[1], sub_sub_item[0], sub_sub_item[2]))
I know about the key = operator.itemgetter() option, but I'm not sure if that would even work for a list like this or where to put it in my for loops.
Any hints to point me in the right direction would be much appreciated.
You can try this:
for item in teamlist:
for sub_item in item:
teamName = item[0]
if user_input == teamName:
sorted_lists = [x for x in zip(*sorted(zip(sub_item), key=lambda x: x[0]))]
for sub_sub_item in sorted_lists[0]:
print(" {:<25} {:^6} {:>8.2f}".format(sub_sub_item[1], sub_sub_item[0], sub_sub_item[2]))
If you are not a big fan of lambda, then you can use operator's itemgetter method which is as:
sorted_lists = [x for x in zip(*sorted(zip(sub_item), key=operator.itemgetter(0)))]
I am not quite certain what you want to sort on, but take a look at the documentation, i.e. https://wiki.python.org/moin/HowTo/Sorting.
You can supply a function to sorted, to determine the key to sort by, so to sort [(2011, 'Chauncey Washington', 47.29080932784636, 1, -1, 0, 0), (2011, 'DeMarco Murray', 90.58014654220617, 164, 897, 2, 1)...] by year you could call
from operator import itemgetter
...
sorted(yourList, key=itemgetter(0))
or with a lambda
sorted(yourList, key= lambda item: item[0] )
For more complex sorts look at the section Sort Stability and Complex Sorts from the link above. Basically you start sorting by the least important keys, and then do stable sorts for the more important sort keys till you reach the most important one.
It looks like the elements within each inner tuple are already in the correct order for sorting (that is, most significant element first), so the simplest is to sort them as you are building teamlist:
for key, val in teamDict.items():
teamlist.append((key, sorted(val)))
This is a simple enough loop that you could make it a list comprehension if you like:
teamlist = [(key, sorted(val)) for key, val in teamDict.items()]
In the code below (for printing salaries in descending order, ordered by profession),
reader = csv.DictReader(open('salaries.csv','rb'))
rows = sorted(reader)
a={}
for i in xrange(len(rows)):
if rows[i].values()[2]=='Plumbers':
a[rows[i].values()[1]]=rows[i].values()[0]
t = [i for i in sorted(a, key=lambda key:a[key], reverse=True)]
p=a.values()
p.sort()
p.reverse()
for i in xrange(len(a)):
print t[i]+","+p[i]
when i put 'Plumbers' in the conditional statement, the output among the salaries of plumbers comes out to be :
Tokyo,400
Delhi,300
London,100
and when i put 'Lawyers' in the same 'if' condition, output is:
Tokyo,800
London,700
Delhi,400
content of CSV go like:
City,Job,Salary
Delhi,Lawyers,400
Delhi,Plumbers,300
London,Lawyers,700
London,Plumbers,100
Tokyo,Lawyers,800
Tokyo,Plumbers,400
and when i remove --> if rows[i].values()[2]=='Plumbers': <-- from the program,
then it was supposed to print all the outputs but it prints only these 3:
Tokyo,400
Delhi,300
London,100
Though output should look something like:
Tokyo,800
London,700
Delhi,400
Tokyo,400
Delhi,300
London,100
Where is the problem exactly?
First of all, your code works as described... outputs in descending salary order. So works as designed?
In passing, your sorting code seems overly complex. You don't need to split the location/salary pairs into two lists and sort them independently. For example:
# Plumbers
>>> a
{'Delhi': '300', 'London': '100', 'Tokyo': '400'}
>>> [item for item in reversed(sorted(a.iteritems(),key=operator.itemgetter(1)))]
[('Tokyo', '400'), ('Delhi', '300'), ('London', '100')]
# Lawyers
>>> a
{'Delhi': '400', 'London': '700', 'Tokyo': '800'}
>>> [item for item in reversed(sorted(a.iteritems(),key=operator.itemgetter(1)))]
[('Tokyo', '800'), ('London', '700'), ('Delhi', '400')]
And to answer your last question, when you remove the 'if' statement: you are storing location vs. salary in a dictionary and a dictionary can't have duplicate keys. It will contain the last update for each location, which based on your input csv, is the salary for Plumbers.
First of all, reset all indices to index - 1 as currently rows[i].values()[2] cannot equal Plumbers unless the DictReader is a 1-based index system.
Secondly, what is unique about the Tokyo in the first row of you desired output and the Tokyo of the third row? When you create a dict, using the same value as a key will result in overwriting whatever was previously associated with that key. You need some kind of unique identifier, such as Location.Profession for the key. You could simply do the following to get a key that will preserve all of your information:
key = "".join([rows[i].values()[0], rows[i].values()[1]], sep=",")
I have two dictionaries in Python. One is a list of teams and their ranks in a league table, with each team being a key and their ranks being the associated value. The other is data about each team, with the team name being a value of a key:
RankDict = {'Tigers':'1','Lions':'2', 'Pumas':'3', 'Wolves':'4'}
TeamDict = {'TeamRecords':[{'Team': 'Lions', 'Location':'Greenville',
'HomeGround':'Trec Park', 'Coach': 'K. Wilson'},
{'Team':'Pumas', 'Location':'Fairlodge',
'HomeGround':'Fairlodge Fields', 'Coach':'G. Kennedy'}]}
What I want to do is print the details from the TeamDict dictionary based on the rank associated with the team in the RankDict but can't just do a straight key comparison because the keys aren't common. Can anyone help me out?
I can't find anything regarding this type of dictionary comparison in the documentation. Maybe it isn't even possible?
The following will print the team, the rank and the team details (sorted by rank):
import operator
RecordsByTeam = {}
for r in TeamDict['TeamRecords']:
RecordsByTeam[r['Team']] = r
for team, rank in sorted(RankDict.items(), key=operator.itemgetter(1)):
print team
print rank
try:
print RecordsByTeam[team]
except KeyError:
print 'No details could be found for team', team
Note that I'm doing a try/except when printing the team details because TeamDict does not contain information about each team.
Also note that you should use integers for the ranks in RankDict, otherwise the above code won't sort properly when there is a team with, e.g., '11' as rank (it will sort: '11', '2', '3', '4').