# Python Exercises

David Stark / Zarkonnen
2 Jun 2012, 10:10 a.m.
A bunch of python exercises centered around reading in data and doing things to it.

Solutions are provided. More "advanced" solutions that do the same thing, generally in fewer lines of code, using a bit more advanced Python features, are also provided. You're not expected to hit on these solutions, but they may be interesting.

## Longest Name

### Problem

The file names.txt contains a list of names, one per line. Write a program that reads in this list and prints the longest name.

### Expected Output

`Vladimir`

### Solutions

```# Reads list of names from file and prints out the longest one.
names_file = open("names.txt", "r") # Open names_file for reading.
longest_name = "" # Any name will be longer than "" when we compare against it.
for l in names_file: # Loop over the lines in the file.
if len(l) > len(longest_name): # If the line is longer than our current champion...
longest_name = l # Make it our new longest name.
names_file.close() # Close the file after reading: important housekeeping.
print longest_name # Print out the longest name.
```
```from __future__ import with_statement
with open("names.txt", "r") as f: # This "with statement" takes care of closing the file afterwards.
```

## Sum of Name Lengths

### Problem

Write a program that prints the sum of the lengths of the names in names.txt.

`38`

### Hints

When a line is read in from a file in Python, it keeps the new-line character at the end. This means that each line, as read in, is one longer than the name it contains.

### Solutions

```# Reads list of names from file and prints the sum of the lengths.
names_file = open("names.txt", "r") # Open names_file for reading.
sum_of_lengths = 0
for l in names_file:
sum_of_lengths = sum_of_lengths + len(l) - 1 # -1 to account for the newline
names_file.close()
print sum_of_lengths
```
```from __future__ import with_statement
with open("names.txt", "r") as f:
print sum(len(l) - 1 for l in f) # (This is a generator expression!)
```

## Average Name Length

### Problem

Write a program that prints the average of the lengths of the names in names.txt.

`4.75`

### Hints

Depending on your version of Python, you may need to turn the number of names in the list into a floating-point number to make sure that when you divide the sum of name lengths by the number of names, you're not using integer division. You do this by using the float function:
```5 / 2 -> 2
5 / float(2) -> 2.5```

### Solutions

```# Reads list of names from file and prints the average of their lengths.
names_file = open("names.txt", "r")
sum_of_lengths = 0
number_of_names = 0
for l in names_file:
sum_of_lengths = sum_of_lengths + len(l) - 1 # -1 to account for the newline
number_of_names = number_of_names + 1
names_file.close()
print sum_of_lengths / float(number_of_names)
```
```from __future__ import with_statement
# Since it would be so nice to have an "avg" function like a the built-in "min"
# and "max" and so on, let's define it here.
def avg(values, key=lambda x: x):
summed = 0
number = 0
for x in values:
summed += key(x)
number += 1
return summed / float(number)
# (This way we're not keeping all names in memory at the same time.)
with open("names.txt", "r") as f:
print avg(f, key=lambda n: len(n) - 1)
```

## Shortest Names

### Problem

Write a program that prints the shortest names in names.txt, one on each line. There is more than one shortest name, so both need to be printed.

### Expected Output

```Ed

Jo```
Note: The reason there is an empty line between "Ed" and "Jo" is that when you read in lines from a file, you get the lines including the new-line character at the end. print then adds a second new line to this.

### Solutions

```# Reads list of names from file and prints out the shortest ones.
names_file = open("names.txt", "r")
shortest_names = [] # List of shortest names. Right now it's empty.
for l in names_file:
if len(shortest_names) == 0: # If we have no names at all, any will do.
shortest_names = [l]
else:
# We do have some names in our list currently. Are they the shortest?
if len(l) < len(shortest_names): # No! This name is shorter!
shortest_names = [l]
else:
if len(l) == len(shortest_names): # This name is the same length.
shortest_names.append(l) # It gets to join the list.
# In any other case, the name must be longer than ones in the list.
names_file.close()
# Print the names:
for n in shortest_names:
print n
```
```# Fun fact: This is actually a worse program in some ways, as it loads the
# entire list of names into memory at once. This can be _very bad_ if there are
# a lot of names.
from __future__ import with_statement
with open("names.txt", "r") as f:
names.sort(key=len)
print "\n".join([n for n in names if len(n) == len(names)])
```

## Names to Lengths

### Problem

Write a program that reads in the names in names.txt and writes out a file at name_lengths.txt containing the length of each name, one per line, in the corresponding order.

```4
4
8
7
2
5
6
2```

### Hints

A new-line character is written as "\n". Converting a number to text is done using the str function. Hence, converting a number x to its string representation ended with a newline is:
`str(x) + "\n"`
Or, even better, using Python's str.format:
`"{0}\n".format(x)`
Also, don't forget to subtract 1 from the length of each line read in to account for the newline character at the end.

### Solutions

```# Reads list of names from file and writes a file with their lengths.
names_file = open("names.txt", "r")
lengths_file = open("name_lengths.txt", "w") # Open name_lengths.txt for writing
for l in names_file:
lengths_file.write(str(len(l) - 1) + "\n")
names_file.close()
lengths_file.close() # Also need to close files after writing to them.
```
```from __future__ import with_statement
with open("names.txt", "r") as f:
with open("name_lengths.txt", "w") as f2:
f2.writelines("{0}\n".format(len(l) - 1) for l in f)
```

## Sort Names

### Problem

Write a program that reads in the names in names.txt and writes out a file at names_sorted.txt containing them sorted alphabetically.

```Anna
Ed
Hans
Janice
Jo
Michael
Susan

### Solutions

```# Reads list of names from file and writes them back out, sorted.
names_file = open("names.txt", "r")
sorted_file = open("names_sorted.txt", "w")
names.sort()
sorted_file.writelines(names)
names_file.close()
sorted_file.close()
```
```from __future__ import with_statement
with open("names.txt", "r") as f:
with open("names_sorted.txt", "w") as f2:
```

## Sort Names by Length

### Problem

Write a program that reads in the names in names.txt and writes out a file at names_sorted_by_length.txt containing them sorted by length.

```Ed
Jo
Hans
Anna
Susan
Janice
Michael

### Solutions

```# Reads list of names from file and writes them back out, sorted by length.
names_file = open("names.txt", "r")
sorted_file = open("names_sorted_by_length.txt", "w")
names.sort(key=len) # We can use the built-in len function to tell sort to sort by length.
sorted_file.writelines(names)
names_file.close()
sorted_file.close()
```
```from __future__ import with_statement
with open("names.txt", "r") as f:
with open("names_sorted_by_length.txt", "w") as f2:
```

## Print names of a particular length

### Problem

Write a program that reads in the names in names.txt and prints all the names that have the length given by the first command-line argument.

```Hans
Anna```

### Hints

Converting a string to an integer number is done using the int function.
You can avoid printing a newline by using print x, instead of print x, or you can get a string without its final character (letter) by using x[0:-1].

### Solutions

```# Reads list of names from file and prints out the ones that have the length
# given in the first parameter.
import sys
names_file = open("names.txt", "r")
required_length = int(sys.argv) # The 0th element is the name of the running script.
for l in names_file:
if len(l) - 1 == required_length:
print l, # The comma prevents print from adding a newline.
names_file.close()
```
```from __future__ import with_statement
import sys
with open("names.txt", "r") as f:
print "".join(n for n in f if len(n) - 1 == int(sys.argv))
```