Playing around with some python syntax, including types and comprehensions.
The first part of this is part of the homework I assigned due Jan 28.
Jim Mahoney
The vernon_1850.csv file looks like this
lastname,firstname,age
Aldrich,Ann,46
Aldrich,Dwight,11
Aldrich,George,1
Aldrich,Henrietta,8
Aldrich,Henry,16
Aldrich,Margaret,6
Aldrich,Moses,58
But there is one catch: the ages are sometimes things like "3/12", which I think means a fraction with 12 in the bottom, so that "3/12" means "three months old".
I am assuming means "three months old", and for those cases need to calculate the number.
I converted this data from which I found at http://us-census.org/pub/usgenweb/census/vt/windham/1850/ , which I found starting from http://www.us-census.org/ .
from typing import List, Tuple, Any, Union, Iterable
import re
Number = Union[int, float]
StrOrNumber = Union[str, int, float]
def convert(value: str) -> StrOrNumber:
""" strip whitespace, convert to int or float if appropriate """
value = value.strip()
try:
if value.isdigit(): # See the string.isdigit docs.
result = int(value)
#print(" int : result = ", result) # Getting this to work took some debugging...
else:
result = float(value)
#print(" float: result = ", value)
except ValueError:
result = value
#print(f" str: result = '{result}'")
try:
# Special case : turn '4/12' into a float 4/12
# I'm using a regular expression to find <digits>/<digits>
if re.match('^(\d+)(/)(\d+)$', result):
(top, bottom) = result.split('/')
result = float(top) / float(bottom)
except:
pass
return result
assert convert('ab \n') == 'ab'
assert convert('12') == 12
assert convert('12.3') == 12.3
assert convert('6/12') == 0.5
def line2values(line: str) -> Tuple[StrOrNumber] :
""" split at commas, strip whitespace, convert to number if approprite """
return tuple(convert(value) for value in line.split(','))
assert line2values('a, bob,1 , 2.3 \n') == ('a', 'bob', 1, 2.3)
def readcsv(filename: str) -> List[dict]:
""" Read a .csv file with fields in the first line """
# and convert to integers where possible
lines = open(filename, 'r').readlines()
result = []
fields = line2values(lines[0])
for line in lines[1:]:
#print(f"line: '{line}'")
values = line2values(line)
result.append({field: value for (field, value) in zip(fields, values)})
return result
def average(values: Iterable[Number]) -> float:
return sum(values) / len(values)
assert average([1, 2, 3.0]) == 2.0
# Read the csv file in and look at the first few entries
# to make sure it looks OK.
people = readcsv('vernon_1850.csv')
print(f"The number of people is {len(people)}.")
n = 10
print(f"The first {n} are :")
for i in range(10):
p = people[i]
print(f" {p['firstname']} {p['lastname']} , {p['age']}")
# Find the average and print it.
average_age = average([p['age'] for p in people])
print(f"Their average age is {average_age:.4f}.")
Click on the link here to read the problem.
Part 1 : How many numbers have (a) increasing digits and (b) two adjacent digits?
-- examples --
yes: 334567 , 333359
no: 345678, 334401
Part 2 : How many numbers from part 1 don't have their two adjacent digits part of a longer run?
-- examples --
no: 333579
yes: 335579
Using puzzle input range 307237 - 769058, the answers should turn out to be 889 and 589.
def integer2tuple(number: int) -> Tuple[int]:
""" Convert i.e. 112234 to i.e. (1, 1, 2, 2, 3, 4) """
return tuple(int(i) for i in str(number))
assert integer2tuple(112234) == (1, 1, 2, 2, 3, 4)
def is_increasing(numbers: Tuple[int]) -> bool:
""" Is this tuple increasing ? """
return all(pair[0] <= pair[1] for pair in zip(numbers[:-1], numbers[1:]))
assert is_increasing((1, 2, 3, 4)) == True
assert is_increasing((1, 1, 1, 1)) == True
assert is_increasing((1, 1, 1, 0)) == False
def has_duplicate(numbers: Tuple[int]) -> bool:
""" Does this tuple have an adjacent pair of the same number ? """
return any(pair[0] == pair[1] for pair in zip(numbers[:-1], numbers[1:]))
assert has_duplicate((1, 2, 3, 4)) == False
assert has_duplicate((1, 1, 3, 4)) == True
assert has_duplicate((1, 3, 3, 3)) == True
def is_part1(number: int) -> bool:
""" Does this integer satisfy the part 1 criteria? """
numbers = integer2tuple(number)
return is_increasing(numbers) and has_duplicate(numbers)
assert is_part1(111111) == True # These three tests are given in the problem description.
assert is_part1(223450) == False
assert is_part1(123789) == False
low = 307237
high = 769058
# How many of the numbers from low to high satisify part1
# ... using len() and a list comprehension.
len([n for n in range(low, high + 1) if is_part1(n)])
# part 2 : two numbers consecutive (a "double"), but not three the same.
def has_double(numbers: Tuple[int]) -> bool:
""" Does this tuple have a pair which is not part of a triple? """
# I want to look for (a,b,c,d) where b==c, a!=b, c!=d.
# But at the ends of the tuple this won't work.
# I'll make it work by adding extra 1st and last "bookend" values of -1,
# then loop with an index i from 0 to 4 back from the end.
# example :
# numbers = (1, 1, 3, 4, 5, 6)
# with_bookends = (-1, 1, 1, 3, 4, 5, 6, -1)
# i=0 1st group : -1, 1, 1, 3 # i.e. "i in range(5)"
# i=4 last group: 4, 5, 6, -1 # and 5 is len(numbers)-1
#
with_bookends = (-1,) + numbers + (-1,)
for i in range(len(numbers) - 1):
(a, b, c, d) = with_bookends[i: i + 4]
if a != b and b == c and c != d:
return True
return False
assert has_double((1, 2, 3, 3, 5, 6)) == True
assert has_double((1, 2, 3, 3, 3, 6)) == False
def is_part2(number: int) -> bool:
""" Does this number satisfy the part 2 rule? """
numbers = integer2tuple(number)
return is_increasing(numbers) and has_double(numbers)
assert is_part2(112233) == True # tests from problem statement
assert is_part2(123444) == False
assert is_part2(111122) == True
# So the part 2 answer is
len([n for n in range(low, high + 1) if is_part2(n)])