Search code examples
pythondatesortingdictionarylist-comprehension

Comparing a combined start and end date to determine ordering


With a Python list of dictionaries which always contains start and end dates, how would you sort the list based on the "combined" start and end dates?

What would be the simplest (most Pythonic) way to obtain the end result with the following criteria - from top to bottom:

  1. Sort by end_date (descending) first and then by start_date (descending).
  2. If there are two objects with the same end_date, then the latest start_date comes first i.e.: then order by the start_date for those items.
  3. Should both the start_date and the end_date be the same, then the ordering of those items is not a concern and can be ignored or remain as is.
import datetime


blah = [
    {"id": 1, "start_date": datetime.date(2021, 5, 1), "end_date": None},
    {"id": 2, "start_date": datetime.date(2013, 2, 1), "end_date": None},
    {"id": 3, "start_date": datetime.date(2017, 1, 1), "end_date": datetime.date(2018, 1, 1)},
    {"id": 4, "start_date": datetime.date(2016, 5, 1), "end_date": datetime.date(2019, 6, 1)},
    {"id": 5, "start_date": datetime.date(2012, 1, 1), "end_date": datetime.date(2015, 1, 1)},
    {"id": 6, "start_date": datetime.date(2008, 1, 1), "end_date": datetime.date(2011, 1, 1)},
    {"id": 7, "start_date": datetime.date(2006, 1, 1), "end_date": datetime.date(2008, 1, 1)},
    {"id": 8, "start_date": datetime.date(2005, 1, 15), "end_date": datetime.date(2010, 1, 15)},
    {"id": 9, "start_date": datetime.date(2002, 1, 15), "end_date": datetime.date(2002, 1, 15)},
    {"id": 10, "start_date": datetime.date(2002, 1, 1), "end_date": datetime.date(2006, 1, 1)},
    {"id": 11, "start_date": datetime.date(2002, 1, 1), "end_date": datetime.date(2006, 1, 1)},
    {"id": 12, "start_date": datetime.date(2001, 2, 1), "end_date": datetime.date(2003, 1, 1)},
    {"id": 13, "start_date": datetime.date(2001, 1, 15), "end_date": datetime.date(2003, 1, 15)},
    {"id": 14, "start_date": datetime.date(1998, 1, 1), "end_date": datetime.date(2001, 1, 1)},
    {"id": 15, "start_date": datetime.date(1997, 1, 15), "end_date": datetime.date(1997, 1, 15)}
]

# Do something here...and return `result`.

result = [
    {"id": 1, "start_date": datetime.date(2021, 5, 1), "end_date": None},
    {"id": 2, "start_date": datetime.date(2013, 2, 1), "end_date": None},
    {"id": 4, "start_date": datetime.date(2016, 5, 1), "end_date": datetime.date(2019, 6, 1)},
    {"id": 3, "start_date": datetime.date(2017, 1, 1), "end_date": datetime.date(2018, 1, 1)},
    {"id": 5, "start_date": datetime.date(2012, 1, 1), "end_date": datetime.date(2015, 1, 1)},
    {"id": 6, "start_date": datetime.date(2008, 1, 1), "end_date": datetime.date(2011, 1, 1)},
    {"id": 8, "start_date": datetime.date(2005, 1, 15), "end_date": datetime.date(2010, 1, 15)},
    {"id": 7, "start_date": datetime.date(2006, 1, 1), "end_date": datetime.date(2008, 1, 1)},
    {"id": 11, "start_date": datetime.date(2002, 1, 1), "end_date": datetime.date(2006, 1, 1)},
    {"id": 10, "start_date": datetime.date(2002, 1, 1), "end_date": datetime.date(2006, 1, 1)},
    {"id": 9, "start_date": datetime.date(2002, 1, 15), "end_date": datetime.date(2002, 1, 15)},
    {"id": 12, "start_date": datetime.date(2001, 2, 1), "end_date": datetime.date(2003, 1, 1)},
    {"id": 13, "start_date": datetime.date(2001, 1, 15), "end_date": datetime.date(2003, 1, 15)},
    {"id": 14, "start_date": datetime.date(1998, 1, 1), "end_date": datetime.date(2001, 1, 1)},
    {"id": 15, "start_date": datetime.date(1997, 1, 15), "end_date": datetime.date(1997, 1, 15)}
]

Solution

  • You can simply sort your data with the appropriate key to satisfy points 1-3; point 4 is automatically satisfied since the sort in Python is guaranteed to be stable:

    result = sorted(blah,
                    reverse = True,
                    key=lambda d:(
                        d["end_date"] if d["end_date"] is not None else datetime.date(2999,12,31),
                        d["start_date"])
                    )