The final exam will cover the whole course, with a few exceptions below, and the format will be like the midterm - closed note, writing the code for short Python functions. Please see the course page for the time and location and other instructions for the exam. This page is just about practice problems.
As with the midterm, we will not grade off for superficial syntax type errors. Also, exams are not generally graded on style or decomposition (those being more homework-project topics), so long as your solution gets the correct output.
We award partial-credit on each problem, so even if solving the whole thing seems like too much, try to write in Python code that solves 1 case to start. This strategy of picking off cases one by one mirrors how we do whole programs, but it's good for exam problems too. Our commitment about syntax means, for example, code that uses lst.add()
instead of the correct lst.append()
or is missing a quote mark can get full credit anyway.
The exam will cover all of the course material, weighted towards post-midterm material. This page does not repeat the midterm study material, so see the midterm-prep document and the midterm itself for those study questions.
Main coding topics since midterm:
As we said for the midterm, it's so tempting to just look over problems and their solution code, but that is not good studying. How to study for a code-writing exam...
1. Select a problem (it's fine to do the same problem repeatedly until you have it cold)
2. Working from a blank piece of paper, try to solve it (make a drawing)
3. When done, compare your answer to the solution .. repeat
The exam will include printed on it a few reminders of python functions we have used. The list is longer than on the midterm.
Of course the key is knowing how to call them. We will not generally grade off for syntax. The emphasis is on passing the right numbers to range() or in a slice, and example problems below all feature that sort of complexity. If a question needs a function from a module, such as random.randrange() or TK create_line(), the question text will include the documentation for that function.
# CS106A Exam Reference Reminder # [square brackets] denote functions listed here that have been # mentioned but we have not used significantly. # Exam questions do not require such functions. General functions: len() int() str() float() range() list() abs() min() max() sorted(lst, key=lambda, reverse=True) String functions: isalpha() isdigit() isspace() isupper() islower() startswith() endswith() find() upper() lower() split() strip() [replace()] [join()] List functions: append() index() map() [extend() pop()] [del] Dict functions: keys() values() items() File functions with open(filename) as f: # with form for line in f: # loop over lines line = line.strip() s = f.read() # read as one big string Lambda: lambda n: 2 * n SimpleImage: # read filename image = SimpleImage(filename) # create blank image image = SimpleImage.blank(width, height) # foreach loop for pixel in image: # range/y/x loop for y in range(image.height): for x in range(image.width): pixel = image.get_pixel(x, y) pixel = image.get_pixel(x, y) # access pixel by x,y pixel has .x .y .red .green .blue pix = image.get_pix(x, y) # pix (r, g, b) tuple form image.set_pix(x, y, pix) Canvas/TK Draw # (The problem statement will mention any graphic functions # you need, so this list is just a reference.) canvas.draw_line(x1, y1, x2, y2) # x,y is upper left, optional color='red' parameter # float values are truncated to int automatically canvas.draw_rect(x, y, width, height) canvas.fill_rect(x, y, width, height) canvas.draw_oval(x, y, width, height) canvas.fill_oval(x, y, width, height) # TK versions canvas.create_line(x1, y1, x2, y2, fill='red') canvas.create_text(x, y, text='str', fill='red', anchor=tkinter.SW) Grid 2D: grid = Grid.build([['row', '0'], ['row', '1']]) grid.in_bounds(x, y) - True if in bounds grid.width, grid.height - properties grid.get(x, y) - returns contents at that x,y, or None if empty grid.set(x, y, value) - sets new value into grid at x,y
The experimental server is loaded with lecture examples and homework warmup problems which make good practice. Try the "practice mode" option.
Below are exam practice problems for the post-midterm material. The problems on this page are on the more difficult end of the range, but that's fine for practice. We also have final exams from previous quarters on the course page.
Each of the following Python expressions evaluates to something without error. Write the resulting Python value on each line marked "result". The last question is a function-trace, write the 3 numbers printed.
>>> 2 + 3 * 3 result: >>> 12 - 2 + 3 result: >>> 2.0 + 3 * 3 result: >>> '2' + '3' result: >>> 7 // 2 result: >>> 6 // 2 * 5 result: >>> s = 'Winter' >>> s[:3] result: >>> s[3:] result: >>> d = {'c': 4, 'a': 5, 'b': 2} >>> sorted(d.items()) result: ## Function trace def cat(x, y): x = 0 return x + y def caller(): x = 3 y = 4 z = cat(y, x) print(x, y, z) # What 3 numbers are printed result: # end of question
Using find/while-loops on strings. See also the parse1 section on the server.
We'll say an at-word is an '@' followed by zero or more alphabetic chars. Find and return the alphabetic part of the first at-word in s, or the empty string if there is none. So 'xx @abc xyz' returns 'abc'. (lecture example)
def at_word(s): pass
not-doing-2023 - we're not doing these double-while problems this quarter.
Given a string s, write a while loop to find
the first alphabetic char in s.
Write a second while loop to find the last digit char
in s. If both of these chars exist and the digit char
is after the alpha char, return the substring
starting at the alpha char and continuing through and including the digit.
Otherwise return the empty string.
So for example with '^^99abc$$123xx'
the first alpha char is 'a'
,
the last digit is '3'
, and so the result is 'abc$$123'
'^^99abc$$123xx' -> 'abc$$123' 'abc1' -> 'abc1' '1abc' -> ''
def alpha1(s): pass
The hottest new cryptocurrency is Yottercoin. A Yottercoin address is marked with a '.yot'
at its end.
Before that there are guaranteed to be 2 digits. You do not need to check that the digits are there. Before the digits, a series of 0 or more of the three chars, 'y' 'o' 't'
, make up the address to be returned. Find and return the first Yottercoin address in s, or None if there is none.
'xx oyot97.yot xx' -> 'oyot' 'xxtoytoy22.yot33' -> 'toytoy' 'xx22.yot33' -> '' 'toytoy' -> None
def yottercoin(s): pass
Reminder: the canvas draw_line function:
canvas.draw_line(x1, y1, x2, y2) - draws a line from x1,y1 to x2,y2
As on the Quilt homework, this function takes in the (left, top, width, height, n) of a figure positioned on the canvas, and draws n lines within that figure.
Draw a blue rectangle at the outer edge of the figure (this code is included).
Given int n which is 2 or more. Draw a series of lines starting at points across the top edge of the figure, all ending at the lower-right corner. The first line should start at the upper left corner, the last line should start at the upper right corner, with the other lines distributed evenly in between.
def draw(canvas, left, top, width, height, n): canvas.draw_rect(left, top, width, height, color='lightblue') pass
Given a canvas, the canvas width and height, and a values list of percent values. Each percent value is in the range 0 .. 100 inclusive. Draw a black vertical line for the first percent value at x=0, the next at x=1, and so on. The width of the canvas will be sufficient to hold all the lines. Each vertical line should be 1 pixel wide, starting at the bottom of the canvas and stretching up towards the top, proportionate to the percentage. When the percent is 0, the line should start and end at the bottom row of pixels. When the percent is 100, the line should end at the top row of pixels.
Reminder: Use canvas.draw_line(x1, y1, x2, y2)
to draw each line.
def draw_percents(canvas, width, height, values): pass
Reminder: the canvas draw_line function:
canvas.draw_line(x1, y1, x2, y2) - draws a line from x1,y1 to x2,y2
This function takes in parameters (left, top, width, height, n, a)
like the Quilt homework, with an additional int parameter a.
Like the Quilt homework, draw a figure with its upper left pixel at left,top, and extending to cover width,height pixels. Draw a blue rectangle at the outer edge of the figure (the draw-rect line is provided below).
Leave a "margin" area of width a at the left and right sides of the figure with no lines drawn in it. The parameter N will be an int, 2 or more. Draw a series of N lines from the top edge to the bottom edge of the figure. The first line should start at the top edge, next to the left margin area, and end at the bottom edge next to the right margin area. The last line should start at the top next to the right margin, ending at the bottom next to the left margin, with the other lines distributed evenly in between.
def draw_a(canvas, left, top, width, height, n, a): canvas.draw_rect(left, top, width, height, color='lightblue') # your code here pass
Do not need to write a def for these questions. For each part, write a 1-line expression to compute the indicated value with: map() or sorted() or min() or max() or a comprehension. You do not need to call list() for these.
# a. Given a list of numbers. # Call map() to produce a result where each # number is multiplied by -1. (or equivalently write a comprehension) >>> nums = [3, 0, -2, 5] # yields [-3, 0, 2, -5] # your expression: # b. Given a list of (x, y) "point" tuples where x and y are int values. # Call map() to produce a result where each (x, y) is replaced # by the sum of x and y. (or equivalently write a comprehension) >>> points = [(1, 2), (4, 4), (1, 3)] # yields [3, 8, 4] # your expression: # c. Given a list of (city-name, zip-code, current-temperature) tuples, like this >>> cities = [('modesto', 95351, 92), ('palo alto', 94301, 80), ...] # Call sorted() to produce a list of these tuples sorted in decreasing # order by temperature. # your expression: # d. Given a non-empty list of (flower-name, scent-score, color-score) tuples # like the following, scores are in the range 1 .. 10 >>> flowers = [('rose', 4, 8), ('orchid', 7, 2), ... ] # Call max() or min() to return the flower tuple with the highest scent score. # your expression:
Say a "mapping" dict has keys which are individual chars like 'a'
,
and its value is another char like 'z'
.
Given a mapping dict and a string s.
Return a new string where every char in s
that is in mapping has been replaced
by its mapped value.
Chars that are not in the mapping leave unchanged.
So for example with the mapping {'a': 'z', 'c': 'x'}
,
the string 'abc'
is changed to 'zbx'
.
def crypt(mapping, s): # your code here
Given a "mapping" dict from the previous problem.
Compute and return a reverse mapping, so if
the original mapping maps 'a'
to 'z'
, the reverse
mapping maps 'z'
to 'a'
.
Assume the values in the mapping dict are all unique.
def reverse_crypt(mapping):
Given a "pairs" list of strings where each string is of the form of a word-colon-number like this:
['apple:27', 'dog:2', 'donut:42', ...]
The word is non-empty and is made of chars that are not a colon.
Create and return a dict with a key for each word, and its value is the sum of all the numbers seen for that word.
def pairsum(pairs):
Given a list of words. Build a counts dict, counting the number of times each 2-char, lowercase prefix appears among the words. Ignore words with length less than 2.
So for example the words ['Coffee', 'BBQ', 'Cottage']
returns the count dict {'co': 2, 'bb': 1}
['Coffee', 'BBQ', 'Cottage'] -> {'co': 2, 'bb': 1} ['meh', 'a', 'me', 'MEAL', ''] -> {'me': 3}
def precount(words): pass
Given a list of non-empty words, produce a "firsts" dict
where each first-char-of-word seen is a key, and its value
is a count dict of words starting with that char.
So ['aaa', 'abb', 'aaa']
yields {'a': {'aaa':2, 'abb':1}}
def count_firsts(words):
An int aqi number rates air quality, with a 0 meaning very clean, and larger numbers correspond to dirtier air. Suppose we have a "states" dict with a key for every state, and the value is a nested dict holding the max observed aqi number for each city, like this:
states = { 'ca': {'palo alto': 234, 'stanford': 200, 'aardvark': 113}, 'tx': {'houston': 321, 'austin': 77}, ... }
a. Write code for a function add_aqi(states, line), which adds a new data point to the passed in states dict (which may be empty) and in all cases returns the dict. The "line" parameter will hold a state name, a city name, and an aqi number all separated by commas, like this:
ca,palo alto,137
Max-rule: do not add an aqi number for a city if that city already has a higher aqi number in the dict. For example, if palo alto already had a number of 150, then trying to add 137 as in the example line above should leave the dict unchanged. In effect, the states dict is storing the max seen aqi for each city.
def add_aqi(states, line):
b. Write two Doctests for the add_aqi() function. The first test should show an aqi being added. The second test should show an aqi not being added because of the max-rule.
# 2 Doctests add_aqi(states-dict, line) >>> add_aqi(...
c. Write code for an average_aqi(states, state) function that takes in the states dict and a state name like 'ca', and returns the average of that city's aqi numbers. Return -1 if the state is not in the data. If a state is in the data, it will have at least one city.
def average_aqi(states, state): # e.g. state == 'ca'
We have a social network, and every user is identified by an id string like '@sal'. We have a "sends" dict which tracks which users across the whole system have sent a message recently. For each user who has sent a message within the last hour, there will be an entry in the sends dict with that user as the key and their most recent recipient as the value. For example with the following dict, we see that '@sal' sent a message to '@alice'.
sends = { '@sal': '@alice', '@miguel': '@sophie', ... }
Write code for the send_score() function below - given two users, a and b, and the sends dict, return a "sending" score as follows: if a sent a message to b, the score is 5. Otherwise, if a sent a message to some other user, who we will call "x", and x sent a message to b, the score is 1. Otherwise the score is 0.
def send_score(sends, a, b): pass
Suppose we have a "foods" dict, where each key is a food, and its value is a len-2 tuple (tasty-score, healthy-score), where each score is in the range 1 .. 10, like this:
foods = { 'coffee': (3, 4), 'oatmeal': (6, 9), 'donut': (10, 1), }
We have a "meals" dict where each key is a meal, and its value is a list of foods for that meal:
meals = { 'breakfast': ['coffee', 'oatmeal'], 'lunch': ['donut'], 'dinner': ['beets', 'donut'] }
Write code that, given the foods and meals dicts, creates and returns a new "healthy" dict which gathers the healthy scores for each planned meal. The healthy dict has the same keys as the meals dict, and the value for each key is a list of the healthy scores of that meal's foods. If a particular food is not present in the foods dict, assume it has a healthy score of 5, e.g. 'beets'
in the example below.
Input foods and meals dicts: foods = { 'coffee': (3, 4), 'oatmeal': (6, 9), 'donut': (10, 1), } meals = { 'breakfast': ['coffee', 'oatmeal'], 'lunch': ['donut'], 'dinner': ['beets', 'donut'] } Computed healthy dict: { 'breakfast': [4, 9], 'lunch': [1], 'dinner': [5, 1] }
def healthy_dict(foods, meals): pass
These problems pull together many important Python coding features into one problem, so they are kind of summary and they are also somewhat realistic practice for things you might want to do in the future.
Suppose we are working on a dumbed-down Tweet technology called Bleet. In a Bleet, all hashtags look like '#omg'
- the hashtag is made of a '#'
char and the 3 following chars. No logic or loop is needed to find the end of the hashtag; it's simply the 3 chars after the '#'
. We have a text file, where each line is made of 2 or more bleets, separated by plus chars '+'
.
hello #omgthere+hi and#idk as if#omg+#idkwhat
Each bleet, e.g. 'hello #omgthere'
, contains exactly 1 hashtag, and other than the hashtag, does not contain any '#'
or '+'
chars.
Process all the lines in the file, building and and returning a dict that has a key for each hashtag string without the leading '#'
, and its value is a list of all the bleets that include that hashtag.
{ 'omg': ['hello #omgthere', 'as if#omg'] 'idk': ['hi and#idk', '#idkwhat'] }
def read_bleets(filename): bleets = {} with open(filename) as f: for line in f: line = line.strip() # remove \n pass return bleets
Suppose you are part of a team handling a sudden surge of package deliveries. You have a data file for each day that looks like this:
ca,94301-0001,94025-1122,94301-9999,... wa,98001-0000,98001-4322,... ...
The parts of each line are separated by commas.
The first word is a 2-char state code.
After the state are 1 or more zip+4 codes,
like '94301-0001'
, each representing one delivery.
The format of the zip+4 is:
5 digit zipcode, dash, 4 digits.
We will treat the zipcode as a string.
Write code to build a "states"
dict with a key for every state,
The value for each state key is a nested dict
which counts the number of times each zipcode
string has a delivery, considering only the zipcode '94301'
part of the zip+4 '94301-0001'
.
{ 'ca': {'94301': 2, '94025': 1, ...} 'wa': {'98001': 5, '98002': 1, ...} }
Once the sates dict is built, write code to print each state code on a line by itself, in increasing alphabetic order. Each state should be followed by its zip/count data, with the zipcodes in increasing order, so the overall print output looks like this:
ca 94025 1 94301 2 94563 5 tx 75001 5 75002 1 75011 2 wa 98001 5 98002 1
This function does not return anything - it prints its output. The standard code to open a file and loop over its lines is provided.
def count_zips(filename): states = {} with open(filename) as f: for line in f: pass
There are many lightning strikes each day across the united states. Each lightning strike is represented by a number 1..100. Suppose you have a data file where each line represents one or more lightning strikes. Each line is made of one or more lightning strike ints followed by the state name, separated by commas like this:
26,1,ca 99,100,100,tx 12,50,ca 44,28,tx
Write a function to read the lines of this file and build a "states" dict with a key for each state, e.g. 'ca', and its value is a list of all that states's int strike numbers. So for example, the 4 lines of text above make this states dict:
{ 'ca': [26, 1, 12, 50], 'tx': [99, 100, 100, 44, 28], }
When all the data is loaded, print the states in alphabetical order, 1 per line, each followed by its list of numbers, followed by the sum of the numbers, like this:
ak [100, 12] 112 ca [26, 1, 12, 50] 89 tx [99, 100, 100, 44, 28] 371 ...
This function does not return anything - it prints its output. The standard code to open a file and loop over its lines is provided.
>>> # Reminder of how to print a list of ints: >>> nums = [1, 2, 3] >>> print(nums) [1, 2, 3]
def parse_lightning(filename): states = {} with open(filename) as f: for line in f: pass
For this problem, we have information about many flights stored in a text file. Each line lists flights from one departure city to one or more arrival cities:
sfo,lax2300,jfk400 lax,sfo5000,pdx900,chi3500 sfo,lax200 ...
The Early Bird computation: Build and return a dict that has a key for each departure city. The value for each departure city is a nested dict that stores the earliest flight time seen for each arrival city from that departure city. An example:
{ 'sfo': {'lax': 200, 'jfk': 400} 'lax': {'sfo': 5000, 'pdx': 900, 'chi': 3500} ... }
Suppose there are many flights from sfo to lax. The above dict encodes that the earliest flight from sfo to lax is at time 200. The earliest flight from sfo to jfk is at time 400, and so on.
def early_bird(filename): """ Build and return an early-bird dict from the lines in the given file. """ # your code
Given a data file where each line is a mixture of non-empty alphabetic words and non-negative ints, all separated by colons, like this:
aaa:123:2:xyz:xxx:44:zz:a xyz:42:199:x:pq:44 aaa:19:999:z ...
The first element on each line is the "prime" for that line, and is always alphabetic. Read through all the lines and build a dict with a key for each prime, and its value is a list of all the int number values from all the lines with that prime. Add every number to the list, even if it is a duplicate. Reminder: for lists a and b, a.extend(b) adds all of b's elements to a.
When all the data is loaded, print the primes in alphabetical order 1 per line, each followed by its numbers in increasing order, 1 per line, like this
aaa 2 19 44 123 999 baker 16 23 ...
def prime_file(filename): primes = {} with open(filename) as f: for line in f: pass
>>> 2 + 3 * 3 result: 11 >>> 2.0 + 3 * 3 result: 11.0 >>> 12 - 2 + 3 result: 13 >>> '2' + '3' result: '23' >>> 7 // 2 result: 3 >>> 6 // 2 * 5 result: 15 >>> s = 'Winter' >>> s[:3] result: 'Win' >>> s[3:] result: 'ter' >>> d = {'c': 4, 'a': 5, 'b': 2} >>> sorted(d.items()) result:[('a', 5), ('b', 2), ('c', 4)] ## Function trace def cat(x, y): x = 0 return x + y def caller(): x = 3 y = 4 z = cat(y, x) print(x, y, z) # What 3 numbers are printed result: 3 4 3
def at_word(s): at = s.find('@') if at == -1: return '' # Advance end over alpha chars end = at + 1 while end < len(s) and s[end].isalpha(): end += 1 word = s[at + 1:end] return word def alpha1(s): pass # Loop to find first alpha begin = 0 while begin < len(s) and not s[begin].isalpha(): begin += 1 # Works without this test: # if begin >= len(s): # return '' # Loop backwards to find last digit. end = len(s) - 1 while end >= 0 and not s[end].isdigit(): end -= 1 # Check if there's nothing if end <= begin: return '' return s[begin:end + 1] def yottercoin(s): yot = s.find('.yot') if yot == -1: return None begin = yot - 3 # Move begin left over y-o-t chars while begin >= 0 and (s[begin] == 'y' or s[begin] == 'o' or s[begin] == 't'): begin -= 1 addr = s[begin + 1:yot - 2] return addr
def draw(canvas, left, top, width, height, n): canvas.draw_rect(left, top, width, height, color='lightblue') for i in range(n): x_add = (i / (n - 1)) * (width - 1) canvas.draw_line(left + x_add, top, left + width - 1, top + height - 1) def draw_percents(canvas, width, height, values): for i in range(len(values)): value = values[i] x = i # delta between y_bottom and y_top of line y_delta = (value / 100) * (height - 1) canvas.draw_line(x, height - 1, x, height - 1 - y_delta) def draw_a(canvas, left, top, width, height, n, a): canvas.draw_rect(left, top, width, height, color='lightblue') for i in range(n): x_add = (i / (n - 1)) * (width - 2 * a - 1) canvas.draw_line(left + a + x_add, top, left + width - a - 1 - x_add, top + height - 1) # ^^ candidate for most complex line of the course
a. [n * -1 for n in nums] b. [pt[0] + pt[1] for pt in points] or map(lambda pt: pt[0] + pt[1], points) c. sorted(cities, key=lambda city: city[2], reverse=True) d. max(flowers, key=lambda flower: flower[1])
def crypt(mapping, s): result = '' for ch in s: if ch in mapping: result += mapping[ch] else: result += ch return result def reverse_crypt(mapping): result = {} for key in mapping.keys(): value = mapping[key] result[value] = key # install reversed return result def pairsum(pairs): counts = {} for pair in pairs: parts = pair.split(':') # or could use .find() word = parts[0] num = int(parts[1]) if word not in counts: counts[word] = 0 counts[word] += num return counts def precount(words): counts = {} for word in words: if len(word) >= 2: pre = word[:2].lower() if pre not in counts: counts[pre] = 0 counts[pre] += 1 return counts def count_firsts(words): first = {} for word in words: ch = word[0] if ch not in first: first[ch] = {} inner = first[ch] if word not in inner: inner[word] = 0 inner[word] += 1 return first # AQI def add_aqi(states, line): parts = line.split(',') state = parts[0] # pull parts into vars city = parts[1] aqi = int(parts[2]) if state not in states: states[state] = {} nested = states[state] # add var to nested # 1. Using "or" to set aqi if city not in nested or nested[city] < aqi: nested[city] = aqi # 2. This way fine too # if city not in nested: # nested[city] = aqi # if nested[city] < aqi: # nested[city] = aqi # test 1 - just add a number to empty {} >>> add_aqi({}, 'ca,palo alto,150') {'ca': {'palo alto': 150}} # test 2 - city already in there with bigger aqi >>> add_aqi({'ca': {'palo alto': 150}}, 'ca,palo alto,99') {'ca': {'palo alto': 150}} def average_aqi(states, state): if state not in states: return -1 nested = states[state] # var to nested sum = 0 for city in nested.keys(): sum += nested[city] return sum / len(nested.keys()) # or could compute the average getting the # numbers out of nested.values() def send_score(sends, a, b): if a in sends: x = sends[a] # a sent to x if x == b: return 5 if x in sends: # x sent to someone if sends[x] == b: return 1 return 0 def healthy_dict(foods, meals): healthy = {} for meal in meals.keys(): # build the scores list for this meal scores = [] for food in meals[meal]: if food in foods: tup = foods[food] scores.append(tup[1]) else: scores.append(5) healthy[meal] = scores return healthy
def read_bleets(filename): bleets = {} with open(filename) as f: for line in f: line = line.strip() # remove \n parts = line.split('+') for bleet in parts: hash = bleet.find('#') tag = bleet[hash + 1:hash + 4] if tag not in bleets: bleets[tag] = [] bleets[tag].append(bleet) return bleets def parse_lightning(filename): states = {} with open(filename) as f: for line in f: pass words = line.split(',') i_state = len(words) - 1 state = words[i_state].strip() # minor: remove \n if state not in states: states[state] = [] nums = states[state] # var to nested for numstr in words[:i_state]: # omit last elem nums.append(int(numstr)) for state in sorted(states.keys()): # could compute sum manually - here we use sum() print(state, states[state], sum(states[state])) def count_zips(filename): states = {} with open(filename) as f: for line in f: pass words = line.split(',') state = words[0] if state not in states: # not seen before: empty-dict states[state] = {} counts = states[state] # var points to nested for word in words[1:]: # slice to skip [0] zip = word[:5] # grab zipcode if zip not in counts: counts[zip] = 0 counts[zip] += 1 # output for state in sorted(states.keys()): print(state) counts = states[state] for zip in sorted(counts.keys()): print(zip, counts[zip]) def early_bird(filename): with open(filename) as f: birds = {} for line in f: parts = line.split(',') depart = parts[0] if depart not in birds: birds[depart] = {} # now go through flights for flight in parts[1:]: # use slice to skip over depart city arrive = flight[:3] # slice of arrive time = int(flight[3:]) # slice of time, int # the key moment inner = birds[depart] if arrive not in inner: inner[arrive] = time else: # works ok without this else: if time < inner[arrive]: # replace with time? inner[arrive] = time return birds def prime_file(filename): primes = {} with open(filename) as f: for line in f: parts = line.split(':') prime = parts[0] # pick out the elements which are numbers nums = [] for part in parts[1:]: # works without slice too if part[0].isdigit(): nums.append(int(part)) if prime not in primes: primes[prime] = [] primes[prime].extend(nums) # now output for prime in sorted(primes.keys()): print(prime) for num in sorted(primes[prime]): print(num) # Not necessary, but the above computation of the nums list # can be done as a very dense 1-line comprehension - either way is fine # nums = [int(part) for part in parts[1:] if part[0].isdigit()]