function tests haven't gone as expected(part of AoC day4)

Viewed 51

I wrote a function that checks if data is correct. Requirements are as follows:

byr-(Birth Year) - four digits; at least 1920 and at most 2002.

iyr (Issue Year) - four digits; at least 2010 and at most 2020.

eyr (Expiration Year) - four digits; at least 2020 and at most 2030.

def check_byr_iyr_eyr(line):
    statement = True
    if line[:3] == "byr":
        if (len(line[line.index(':')+1:]) != 4 or
        1920 > int(line[line.index(':')+1:]) > 2002 ):
            statement = False
    elif line[:3] == "iyr":
        if (len(line[line.index(':')+1:]) != 4 or
        2010 > int(line[line.index(':')+1:]) > 2020 ):
            statement = False
    elif line[:3] == "eyr":
        if (len(line[line.index(':')+1:]) != 4 or
        2020 > int(line[line.index(':')+1:]) > 2030 ):
            statement = False
    return statement


list = ['byr:1919', 'iyr:2010', 'eyr:2021', 'iyr:2019', 'iyr:1933',
        'byr:1946', 'iyr:1919', 'eyr:2005']

for i in list:
    print(check_byr_iyr_eyr(i))

'''
    expected result:
        False
        True
        True
        True
        False
        True
        False
        False
'''

and results of checking provided samples should be like in that multi-line comment "expected results", but unfortunately a result is always True.

I don't know what I'am doing wrong - conditions seems good to me...

3 Answers

Consider this line:

1920 > val > 2002

It is the same result as:

val < 1920 and val > 2002

It means that val is both less than 1920, and greater than 2002, which can never be true.

An elegant solution, using too many if statements is not DRY (don't repeat yourself):

def check_byr_iyr_eyr(line):
    # split the string on the colon to get the two parts
    prefix, year = line.split(':')
    # getting around python's lack of case statements with a dictionary
    cases = {
        'byr': {'min': 1920, 'max': 2002},
        'iyr': {'min': 2010, 'max': 2020},
        'eyr': {'min': 2020, 'max': 2030},
    }
    # get the corresponding min and max and check if the year is inclusively between them
    # (note the <= instead of <)
    return cases[prefix]['min'] <= int(year) <= cases[prefix]['max']


data = ['byr:1919', 'iyr:2010', 'eyr:2021', 'iyr:2019', 'iyr:1933',
        'byr:1946', 'iyr:1919', 'eyr:2005']

for i in data:
    print(check_byr_iyr_eyr(i))

Output:

False
True
True
True
False
True
False
False

There are two problems with your function:

  1. Instead of doing complicated string slicing to check what type it is, just do string in line. It's much simpler and readable.

  2. The expression 5 > x > 7, as wim mentioned, is equivalent to an and statement. Since x cannot be both smaller than 5 and greater than 7, this is never True. Do x > 7 or x < 5 instead.

Below is the corrected code which has the correct output:

def check_byr_iyr_eyr(line):
    statement = True
    if "byr" in line:
        if (len(line[line.index(':')+1:]) != 4 or
        int(line[line.index(':')+1:]) > 2002 or 
        int(line[line.index(':')+1:]) < 1920):
            statement = False
    elif "iyr" in line:
        if (len(line[line.index(':')+1:]) != 4 or
        int(line[line.index(':')+1:]) > 2020 or 
        int(line[line.index(':')+1:]) < 2010):
            statement = False
    elif "eyr" in line:
        if (len(line[line.index(':')+1:]) != 4 or
        int(line[line.index(':')+1:]) > 2030 or
        int(line[line.index(':')+1:]) < 2020):
            statement = False
    return statement


list = ['byr:1919', 'iyr:2010', 'eyr:2021', 'iyr:2019', 'iyr:1933',
        'byr:1946', 'iyr:1919', 'eyr:2005']

for i in list:
    print(check_byr_iyr_eyr(i))

'''
    expected result:
        False
        True
        True
        True
        False
        True
        False
        False
'''

Check out Tenacious B's answer for a much more elegant solution!

Related