4

I want to be able to sort a list of classes by one attribute, which happens to be another class Date. I have done some research and want to use the sorted(list, key=lambda x:date) method of sorting, but seeing as date is a class in itself how can I write an __lt__ function in date to allow me to sort chronologically?

I want something along the lines of:

if self.year!= other.year:
    return self.year < other.year
elif self.month != pther.month
...

and so on.

Here is my Date class:

class Date:
    def __init__(self, month, day, year, minute, hour, string):
        self.month = month
        self.day = day
        self.year = year
        self.minute = minute
        self.hour = hour
        self.string = string

I should probably mention that this is the first time I have ever used Python, so I'm not very good at this.

Thanks in advance!

Sebastian Wozny
  • 16,943
  • 7
  • 52
  • 69
PresumablyZach
  • 99
  • 3
  • 12

2 Answers2

5

The easy way to compare two complex data structures is to compare tuples of their attributes, in the proper sort order. Try this:

class Date:
    def __init__(self, month, day, year, minute, hour, string):
        self.month = month
        self.day = day
        self.year = year
        self.minute = minute
        self.hour = hour
        self.string = string
    def __lt__(self, other):
        return (self.year, self.month, self.day, self.hour, self.minute) < \
               (other.year, other.month, other.day, other.hour, other.minute)

assert Date(4, 15, 2016, 30, 12, '') < \
       Date(4, 16, 2016, 0, 0, '') < \
       Date (1, 1, 2017, 59, 23, '')

assert not (Date(4, 16, 2016, 0, 0, '') < Date(4, 15, 2016, 30, 12, ''))

Of course, this only implements <. Depending upon the nature of your code, you may want to implement all of the other comparison functions, >, ==, !=, etc. One convenient way to do this is to use the @functools.total_ordering class decorator.

Reference:

Community
  • 1
  • 1
Robᵩ
  • 163,533
  • 20
  • 239
  • 308
  • I think you need parentheses there. – user2357112 Feb 18 '17 at 00:47
  • Where, precisely? And why do you think so? – Robᵩ Feb 18 '17 at 00:48
  • Around your tuples. `<` has higher precedence than the comma, so instead of a boolean, you're actually returning a 9-element tuple with a `self.minute < other.year` boolean in the middle. – user2357112 Feb 18 '17 at 00:49
  • Also, it'd be a good idea to implement `__eq__` and put [`functools.total_ordering`](https://docs.python.org/3/library/functools.html#functools.total_ordering) on this class to get the rest of the comparison methods, or at least mention that it's important to remember them in real code. – user2357112 Feb 18 '17 at 00:53
  • @user2357112 - Agreed. – Robᵩ Feb 18 '17 at 00:58
1

I propose adding a datetime object to the class definition:

import datetime as dt, operator as op

class MyDate(object):
    def __init__(self, month, day, year, minute, hour, string):
        self.month = month
        self.day = day
        self.year = year
        self.minute = minute
        self.hour = hour
        self.string = string
        self.dt = dt.datetime(self.year,self.month,self.day,self.hour,self.minute)

l = []
l.append(MyDate(2,17,2017,33,16,'somestring')) # not sure what `string` should be
l.append(MyDate(2,17,2017,37,16,'anotherstring'))
l.append(MyDate(2,17,2017,38,16,'yetanotherstring'))

Then you can use operator.attgetter to sort, e.g.:

>>> sorted(l,key=op.attrgetter('dt'),reverse=True)
[<__main__.MyDate object at 0x000000000A4C84A8>, 
 <__main__.MyDate object at 0x000000000A4C8208>, 
 <__main__.MyDate object at 0x000000000A4C8978>]
mechanical_meat
  • 163,903
  • 24
  • 228
  • 223