Home > Blockchain >  Finding the earliest date in list of custom date objects
Finding the earliest date in list of custom date objects

Time:11-11

Given a list of date objects I need to find the date that comes earliest in a file

I have written the following code to do this:

return min(dates, key=lambda x: x.year)

Each object has a year, month, and day attribute. I must use a custom class which I have created (at bottom of post)

This code works until there are two dates with the same year. If two dates have the same year it returns which one comes first in the list which is not what is expected.

For example, these two dates:

1988 10 10
1988 09 03

The function should return 1988 09 03 but instead it returns 1988 10 10 as it comes first.

How do I compare multiple values of an object in python?

I have also tried this

return min(min(min(dates, key=lambda x: x.year), key=lambda x: x.month), key=lambda x: x.day)

but it doesn't seem to work

I have also tried

return min(dates)

but this simply returns the last date in the list

sample of dates file:

1954 10 04
1987 12 20
1993 3 10
1996 7 29
1994 5 30
1993 6 9
1989 12 21
2001 6 30
1995 6 14
2002 2 28
1988 6 21
class Date(object):
    def __init__(self, year, month=1, day=1):
        self.year = year
        self.month = month
        self.day = day

    def __str__(self):
        return '{}/{}/{}'.format(self.year, self.month, self.day)

    def __lt__(self, other):
        return (self.year, self.month, self.day) < (other.year, other.month, other.day)

    def __gt__(self, other):
        return (self.year, self.month, self.day) > (other.year, other.month, other.day)

    def __eq__(self, other):
        return (self.year, self.month, self.day) == (other.year, other.month, other.day)

    def __le__(self, other):
        return (self.year, self.month. self.day) <= (other.year, other.month, other.day)

    def __ge__(self, other):
        return (self.year, self.month, self.day) >= (other.year, other.month, other.day)

    def __ne__(self, other):
        return (self.year, self.month, self.day) != (other.year, other.month, other.day)

Parsing:

def parse_dates(content):
    dates = []
    for line in content:
        line_date = line.strip().split()
        dates.append(Date(line_date[0], line_date[1], line_date[2]))

    return dates

The file is simply opened and then passed through this which returns a list of date objects

Then, it is passed into this to return the earliest date:

def find_earliest(dates):
    return min(dates)
The file where the dates are retrieved from is shown above

Solved I hadn't casted the dates to an int so they stayed as strings, thus the program was comparing strings not actual number values. I simply casted everything to an int then created the date object.

CodePudding user response:

If you don't cast to int in your Date constructor, you need to do it in your parsing:

def parse_dates(content):
    dates = []
    for line in content:
        line_date = line.strip().split()
        dates.append(Date(*map(int, line_date)))

    return dates

or as a one-liner:

def parse_dates(content):
    return [Date(*map(int, line.strip().split())) for line in content]

If you don't want to define all the dunder comparsion methods, you can use functools.total_ordering:

from functools import total_ordering

@total_ordering
class Date(object):
    def __init__(self, year, month=1, day=1):
        #cast your values to ints
        self.year  = int(year)
        self.month = int(month)
        self.day   = int(day)

    def __str__(self):
        # using f-string and padding
        # to get "1994/02/07" instead of "1994/2/7"
        return f'{self.year}/{self.month:02}/{self.day:02}'

    def __eq__(self, other):
        return self.year  == other.year
           and self.month == other.month
           and self.day   == other.day

    def __lt__(self, other):
        # works because `or` short-circuits on first True
        return self.year  < other.year
            or self.month < other.month
            or self.day   < other.day
  • Related