0

So the goal here for me is to take three dictionaries, and print it off in this format:

Name: "Name"

Homework: "Average of list"

Quizzes: "Average of list"

Tests: "Average of list"

I have the 3 dictionaries included in a list and I'm having trouble digging down. I'm trying to go step by step here.

So after viewing this code:

lloyd = {
    "name": "Lloyd",
    "homework": [90.0, 97.0, 75.0, 92.0],
    "quizzes": [88.0, 40.0, 94.0],
    "tests": [75.0, 90.0]
}
alice = {
    "name": "Alice",
    "homework": [100.0, 92.0, 98.0, 100.0],
    "quizzes": [82.0, 83.0, 91.0],
    "tests": [89.0, 97.0]
}
tyler = {
    "name": "Tyler",
    "homework": [0.0, 87.0, 75.0, 22.0],
    "quizzes": [0.0, 75.0, 78.0],
    "tests": [100.0, 100.0]
}

students = [lloyd,alice,tyler]

def compute_grades(ourstudents):
    for student in ourstudents:
        print "Name: " + student["name"]
        print "Homework: ", sum(student["homework"]) / len(student["homework"])
        print "Quizzes: ", sum(student["quizzes"]) / len(student["quizzes"])
        print "Tests: ", sum(student["tests"]) / len(student["tests"])

compute_grades(students)

Is there a way I could just do a basic check so this will work on ANY dictionary?

For instance...

  1. Is the key definition a string? If so, print it off!
  2. Is the key definition a list? If so, run this averaging function and print off the result.

I basically just want to minimalize the hard coding in this and come up with a more elegant solution. Any help is appreciated!

Nicholas Hazel
  • 3,758
  • 1
  • 22
  • 34
  • `len()` returns an Integer, you'll suffer from Integer division (rounding). Is that fine with you? if not do: `float(len(item["homework"]))` and then convert that to String. – Aleksander Lidtke Sep 07 '13 at 17:31
  • I'm trying to think bigger and not have to ever define "homework" or "tests" or "name", other than in the dictionary itself. – Nicholas Hazel Sep 07 '13 at 17:34
  • 1
    When printing, you don't have to convert everything to string and add it all together. Instead of `print 'homework: ' + str(123)`, you can write `print 'homework:', 123`. Just give print some items separated with a comma, and it converts everything to string and adds a space between the items automatically. – Bas Swinckels Sep 07 '13 at 17:35
  • Why not use `Dictionary.keys()` then? Also, a list cannot be a key in a dictionary (just tried it). The documentations says: `dictionaries are indexed by keys, which can be any immutable type; strings and numbers can always be keys.`. So I don't think you have to worry about that. – Aleksander Lidtke Sep 07 '13 at 17:38
  • 1
    Just a style comment: I wouldn't write `for item in ourstudents:`, but `for student in ourstudents:`. In this way, you dont have to guess later in the code what `item` is. Using singular and plural words in similar cases really helps the readability, e.g. `for line in lines: ...`. – Bas Swinckels Sep 07 '13 at 17:39
  • Well, I know the keys have to be immutable, but the definitions don't. And that definition or value of the key is what I'm trying to scrub over to see if we should just print it off or run an average function on it. – Nicholas Hazel Sep 07 '13 at 17:44
  • Right, there's two ways to do this. One is to `try` to do something and then catch the error (e.g. try to run an averaging function on an entry and, if it turns out to be string, just print it). – Aleksander Lidtke Sep 07 '13 at 17:47
  • Another way would be to check the `type(entry)` and see what that is and then act accordingly (build a set of `if` and `elif` statements to handle the potential cases). – Aleksander Lidtke Sep 07 '13 at 17:48

4 Answers4

3

You can do something like this, however there is no error checking or key sorting at all:

lloyd = {
    "name": "Lloyd",
    "homework": [90.0, 97.0, 75.0, 92.0],
    "quizzes": [88.0, 40.0, 94.0],
    "tests": [75.0, 90.0]
}
alice = {
    "name": "Alice",
    "homework": [100.0, 92.0, 98.0, 100.0],
    "quizzes": [82.0, 83.0, 91.0],
    "tests": [89.0, 97.0]
}
tyler = {
    "name": "Tyler",
    "homework": [0.0, 87.0, 75.0, 22.0],
    "quizzes": [0.0, 75.0, 78.0],
    "tests": [100.0, 100.0]
}

students = [lloyd,alice,tyler]

def average(data):
    return sum(data) / float(len(data))

def compute_grades(students):
    for student in students:
        print('-'*23)
        for key, value in student.items():
            if isinstance(value, str):
                formatted_value = value
            else:
                formatted_value = average(value)
            print("%s: %s" % (key.title(), formatted_value))

compute_grades(students)
tamasgal
  • 24,826
  • 18
  • 96
  • 135
  • You sir answered my question perfectly. I was wondering how to set everything as variables so I could pass any dictionary into it... very well done - to a point where an explanation is not even necessary. Bravo! – Nicholas Hazel Sep 07 '13 at 17:51
  • I would add an sorted around student.keys() as the order is unpredictable otherwise – Xavier Combelle Sep 07 '13 at 17:53
  • You're welcome Nicholas, although the code is far from elegant ;-) but it provides one possible outline. @XavierCombelle of course, there is more to tweak, as I mentioned in the first line of my posting (according error checking and sorting). – tamasgal Sep 07 '13 at 17:55
0

You could wrap it in a try catch block

def compute_grades(ourstudents):
    for student in ourstudents:
        for field, value in student.items():
            try:
                average=sum(value)/float(len(value))
                print "{0}: {1}".format(field.title(), average)
            except TypeError:
                print "{0}: {1}".format(field.title(), value)
Curt
  • 1,394
  • 9
  • 16
  • Very interesting... I haven't had much time to play with try and except, but it appears to be an elegant solution to what I'm trying to accomplish. Nice answer! I'll have to research more on this :-P – Nicholas Hazel Sep 07 '13 at 18:15
0

This appears to be working flawlessly:

lloyd = {
    "name": "Lloyd",
    "performance": [16.0, 19.0, 25.0, 14.4, 24.5, 16.2],
    "homework": [90.0, 97.0, 75.0, 92.0],
    "quizzes": [88.0, 40.0, 94.0],
    "tests": [75.0, 90.0]
}
alice = {
    "name": "Alice",
    "extra_credit": "OVER 9000!!!!",
    "research": [16, 24, 56, 78, 23],
    "missed_days": [1, 1, 2, 1],
    "homework": [100.0, 92.0, 98.0, 100.0],
    "quizzes": [82.0, 83.0, 91.0],
    "tests": [89.0, 97.0]
}
tyler = {
    "name": "Tyler",
    "homework": [0.0, 87.0, 75.0, 22.0],
    "quizzes": [0.0, 75.0, 78.0],
    "tests": [100.0, 100.0]
}

students = [lloyd,alice,tyler]
def average(data):
    return sum(data) / float(len(data))
def compute_grades(ourstudents):
    for student in ourstudents:
        print "-" * 15
        print student["name"]
        for field, value in student.items():
            try:
                average=sum(value)/float(len(value))
                print "{0}: {1}".format(field.title(), average)
            except TypeError:
                pass
compute_grades(students)

Based on @Kurtis input and testing with random extra information

Nicholas Hazel
  • 3,758
  • 1
  • 22
  • 34
-1

This appears to work flawlessly:

lloyd = {
    "name": "Lloyd",
    "performance": [16.0, 19.0, 25.0, 14.4, 24.5, 16.2],
    "homework": [90.0, 97.0, 75.0, 92.0],
    "quizzes": [88.0, 40.0, 94.0],
    "tests": [75.0, 90.0]
}
alice = {
    "name": "Alice",
    "extra_credit": "OVER 9000!!!!",
    "research": [16, 24, 56, 78, 23],
    "missed_days": [1, 1, 2, 1],
    "homework": [100.0, 92.0, 98.0, 100.0],
    "quizzes": [82.0, 83.0, 91.0],
    "tests": [89.0, 97.0]
}
tyler = {
    "name": "Tyler",
    "homework": [0.0, 87.0, 75.0, 22.0],
    "quizzes": [0.0, 75.0, 78.0],
    "tests": [100.0, 100.0]
}

students = [lloyd,alice,tyler]
def average(data):
    return sum(data) / float(len(data))
def compute_grades(students):
    for student in students:
        print "-" * 15
        print student["name"]
        for key in student.keys():
            if isinstance(student[key], str):
                key_value = student[key]
            else:
                key_value = average(student[key])
            if key != "name":
                print("%s: %s" % (key.title(), key_value))
compute_grades(students)

Based on @septi input and testing with random extra information

Nicholas Hazel
  • 3,758
  • 1
  • 22
  • 34