Names, Salaries & Records
The syllabus's second named program: a dictionary of employees and their salaries, and the questions you would actually ask of one. It is the same handful of shapes as the tuple chapter — a total, a champion, a search, a filter — written against keys and values instead of rows.
1The dictionary, and why it is one
# name -> salary
salary = {'Riya': 42000, 'Amit': 55000, 'Sara': 38000, 'John': 61000}
print(salary['Amit'])
print(len(salary))55000 4
(('Riya', 42000), ('Amit', 55000), …) holds exactly the same facts. The dictionary earns its place on one question: what does Amit earn? The dictionary answers it in one step, salary['Amit']. The tuple has to be searched row by row. Whenever the data is naturally “look this up by name”, that is the signal for a dictionary.2Program 1 — print the payroll
# every employee and what they earn
salary = {'Riya': 42000, 'Amit': 55000, 'Sara': 38000, 'John': 61000}
for name, pay in salary.items():
print(name, '->', pay)Riya -> 42000 Amit -> 55000 Sara -> 38000 John -> 61000
With f-strings the columns can be made to line up, which matters the moment a report is more than four lines long:
# widths, so the numbers line up on the right
salary = {'Riya': 42000, 'Amit': 55000, 'Sara': 38000, 'John': 61000}
for name, pay in salary.items():
print(f'{name:<8}{pay:>8}')Riya 42000 Amit 55000 Sara 38000 John 61000
{name:<8} pads the name to eight characters on the left, and {pay:>8} pushes the number to the right of an eight-wide column.
3Program 2 — the total and the average
Find the company's monthly salary bill and the average salary.
The names play no part in this answer, so this is the one place .values() is exactly right:
# only the numbers matter here
salary = {'Riya': 42000, 'Amit': 55000, 'Sara': 38000, 'John': 61000}
total = 0
for pay in salary.values():
total = total + pay
print('Total salary bill:', total)
print('Average salary: ', total / len(salary))Total salary bill: 196000 Average salary: 49000.0
len(salary) counts the pairs — four employees — which is the number to divide by. And the built-in version is two lines:
salary = {'Riya': 42000, 'Amit': 55000, 'Sara': 38000, 'John': 61000}
print(sum(salary.values()))
print(sum(salary.values()) / len(salary))196000 49000.0
4Program 3 — who is paid the most?
Find the highest-paid employee, by name.
Now the name is part of the answer, so .values() is useless and it has to be .items(). Two champions again, moving together:
# the champion program, over key-value pairs
salary = {'Riya': 42000, 'Amit': 55000, 'Sara': 38000, 'John': 61000}
top_name = ''
top_pay = 0
for name, pay in salary.items():
if pay > top_pay:
top_pay = pay
top_name = name
print('Highest paid:', top_name, '-', top_pay)Highest paid: John - 61000
top_pay = 0Safe here, because a salary is never negative or zero. On data that can be negative — a profit-and-loss table — start from a real entry instead, as the tuple chapter did.
top_name = nameInside the same if, so the pair never comes apart. Update the pay and forget the name and the program reports the right figure against the wrong person.
max(salary.values()) gives 61000 and not John. It hands back the biggest value with no idea which key it came from — which is the same limitation as max() on a tuple. The moment the answer has to name something, you are back to the loop.5Program 4 — a lookup that does not crash
Print what one named employee earns, or say they are not on the payroll.
A missing key is not a polite None — it stops the program:
salary = {'Riya': 42000, 'Amit': 55000, 'Sara': 38000, 'John': 61000}
print(salary['Kabir'])Traceback (most recent call last):
File "lookup_crash.py", line 3, in <module>
print(salary['Kabir'])
~~~~~~^^^^^^^^^
KeyError: 'Kabir'There are two ways round it, and both are worth having. Ask first:
# check the key exists before reaching for it
salary = {'Riya': 42000, 'Amit': 55000, 'Sara': 38000, 'John': 61000}
wanted = 'Sara'
if wanted in salary:
print(wanted, 'earns', salary[wanted])
else:
print(wanted, 'is not on the payroll')Sara earns 38000
Kabir is not on the payroll
Or hand get() something to say instead:
salary = {'Riya': 42000, 'Amit': 55000, 'Sara': 38000, 'John': 61000}
print(salary.get('Kabir', 'not on the payroll'))not on the payroll
in when the two cases need different sentences; get() when a stand-in value will do. get() shines when the fallback is a number that keeps the arithmetic working — salary.get(name, 0) lets a total carry on past an unknown employee instead of stopping.6Program 5 — everyone earning above a figure
# a filter and a counter in one walk
salary = {'Riya': 42000, 'Amit': 55000, 'Sara': 38000, 'John': 61000}
limit = 50000
count = 0
for name, pay in salary.items():
if pay > limit:
print(name, 'earns', pay)
count = count + 1
print(count, 'employees earn more than', limit)Amit earns 55000 John earns 61000 2 employees earn more than 50000
The printing happens inside the loop because there is one line per matching employee. The count is printed after it, because there is one of those for the whole payroll — the same rule as the search page, in a smaller form.
7Program 6 — give everyone a 10% raise
A dictionary can be changed while you walk it — unlike a tuple — as long as you only change the values:
# 10% more for everyone
salary = {'Riya': 42000, 'Amit': 55000, 'Sara': 38000, 'John': 61000}
print('Before:', salary)
for name in salary:
salary[name] = salary[name] + salary[name] * 10 // 100
print('After: ', salary)Before: {'Riya': 42000, 'Amit': 55000, 'Sara': 38000, 'John': 61000}
After: {'Riya': 46200, 'Amit': 60500, 'Sara': 41800, 'John': 67100}.values() or .items(). Inside for name, pay in salary.items():, the line pay = pay + … changes only the loop variable and leaves the dictionary exactly as it was — the same trap as for m in marks: m = m + 5 on a list. The assignment has to be salary[name] = ….RuntimeError: dictionary changed size during iterationIf new keys are needed, collect them in a second dictionary during the walk and
update() afterwards.8Program 7 — when one employee has more than one fact
A salary is one number. A real record has a department, a joining date, a grade. The value can be a dictionary of its own:
# the value is itself a dictionary
staff = {
'Riya': {'dept': 'Sales', 'salary': 42000},
'Amit': {'dept': 'Tech', 'salary': 55000},
}
for name, info in staff.items():
print(name, 'works in', info['dept'], 'and earns', info['salary'])Riya works in Sales and earns 42000 Amit works in Tech and earns 55000
info holds a whole inner dictionary each round, so info['dept'] reaches inside it — exactly the stacking that student[2][1] did on nested tuples. staff['Riya']['dept'] is the same thing written in one go.
9Recap
salary['Amit'] is one step. The same data as a tuple of rows would have to be searched.
max(salary.values()) gives 61000 and cannot tell you it was John's.
Guard with `if name in salary:` or use salary.get(name, fallback).
salary[name] = … is fine. Adding or deleting a key raises RuntimeError: dictionary changed size during iteration.
- 1
Find the lowest-paid employee by name.
Hint · Program 3 with
<, and a startingtop_paythat no real salary can beat — or start from a real entry. - 2
Print everyone whose name starts with ‘S’.
Hint · The keys are enough for the test, so
for name in salary:works — withsalary[name]if you want to print the figure too. - 3
Give a raise only to those earning below the average.
Hint · Two passes: work the average out first, then walk the keys and write back with
salary[name] = …. - 4
Ask for a name with
input()and print that person's salary, or a polite message.Hint · Program 4. Names are case-sensitive as keys, so decide whether
'riya'should count. - 5
From the
staffdictionary, print the total salary per department.Hint · A second dictionary keyed by department, filled with
totals[d] = totals.get(d, 0) + info['salary'].
Why does the highest-paid program need .items() rather than .values()?
What happens to the dictionary in `for name, pay in salary.items(): pay = pay + 1000`?
Which of these is not allowed while looping over a dictionary?