1

My objective is to get the next closest date (in the future, not past) to today's date from a list. For simplicity's sake, the list (in the format of e.g. 2017-01-31; YYYY-MM-DD) is each football game in the season and I am trying to create a script that in part finds the "next" football game.

I have searched the internet and Stack Overflow for answers and found a promising post, however the solutions provided are using a different format and when I try to tailor it to mine, it trips exceptions.

My logic includes parsing an RSS feed, so I am just going to provide the raw list instead. With this in mind, my simplified code is as follows:

today = str(datetime.date.today())
print(today)

scheduledatelist = ['2017-09-01', '2017-09-09', '2017-09-16', '2017-09-23', '2017-09-30', '2017-10-07', '2017-10-14', '2017-10-21', '2017-10-27', '2017-11-11', '2017-11-18', '2017-11-25']
scheduledatelist = list(reversed(scheduledatelist)) #purpose: to have earliest dates first

This is my attempt at adapting the previous post's solution (I am not well versed in functional programming, so I may not be adapting it right):

get_datetime = lambda s: datetime.datetime.strptime(s, "%Y-%m-%d")
base = get_datetime(today)
later = filter(lambda d: today(d[0]) > today, scheduledatelist)
closest_date = min(later, key = lambda d: today(d[0]))
print(closest_date)

Regardless of my attempt (which may not be the best in my situation as it changes the format and I need the end value to still be YYYY-MM-DD), is there an easier way of doing this? I need that next game (closest to today) value as that will continue on to be used in my logic. So to recap, how can I find the closest date in my list, looking toward the future, from today. Thank you for your help!

Azat Ibrakov
  • 9,998
  • 9
  • 38
  • 50
J. Squillaro
  • 155
  • 2
  • 13

2 Answers2

3

You can do:

min(scheduledatelist, key=lambda s: 
                  datetime.datetime.strptime(s, "%Y-%m-%d").date()-datetime.date.today())

For the single closest date to today.

You can use the same function to sort by distance from today:

sorted(scheduledatelist, key=lambda s: 
              datetime.datetime.strptime(s, "%Y-%m-%d").date()-datetime.date.today())

And the returned list will be in increasing distance in days from today. Works if the dates are before or after today.

If you want only dates in the future, filter out the dates in the past. Since the date strings are in ISO 8601 format, you can compare lexically:

min([d for d in scheduledatelist if d>str(datetime.date.today())], key=lambda s: 
              datetime.datetime.strptime(s, "%Y-%m-%d").date()-datetime.date.today())
dawg
  • 98,345
  • 23
  • 131
  • 206
  • Option #3 did the trick, as I am looking for future dates but will definitely keep the others in mind as I'll definitely find a use for them! Thank you so much for your help! One last thing: is it possible for me to hardcode a date into the "today" part so I can make sure everything is sound for developer purposes? If I try to replace datetime.date.today() with 2017-10-27 to test, it obviously doesn't work. Is there an easy and temporary test work around? Thanks! – J. Squillaro Aug 07 '17 at 17:12
  • 1
    Yes. Just use `datetime.datetime.strptime('1941-12-07', "%Y-%m-%d").date()` or whatever infamous date you want to use. For the filter, just use the actual date string instead of `str(datetime.date.today())` – dawg Aug 07 '17 at 17:15
2

first of all let's create datetime.date objects from strings using datetime.datetime.strptime and datetime.datetime.date methods since datetime.date objects are ordered and easier to work with:

date_format = '%Y-%m-%d'
dates = [datetime.datetime.strptime(date_string,
                                    date_format).date()

then let's filter out dates that take place in future (after today)

today = datetime.date.today()
future_dates = [date
                for date in dates
                if date >= today]

then we can simply find next closest date using min

next_closest_date = min(future_dates)

which gives us

>>>next_closest_date
2017-09-01

for given example


WARNING

If there is no dates going after today this will cause error like

ValueError: min() arg is an empty sequence

if it's ok then we can leave it, but if we don't want to get errors – we can specify default value for min in case of empty sequence like

next_closest_date = min(future_dates, default=None)

Finally we can write a function as follows

import datetime


# `default` value is returned when there is no future date strings found
def get_next_closest_date(date_strings, date_format, default=None):
    today = datetime.date.today()
    dates = [datetime.datetime.strptime(date_string,
                                        date_format).date()
             for date_string in date_strings]
    future_dates = [date
                    for date in dates
                    if date >= today]
    return min(future_dates, default)

and use it like

scheduledatelist = ['2017-09-01', '2017-09-09', '2017-09-16', '2017-09-23',
                    '2017-09-30', '2017-10-07', '2017-10-14', '2017-10-21',
                    '2017-10-27', '2017-11-11', '2017-11-18', '2017-11-25']
next_closest_date = get_next_closest_date(date_strings=scheduledatelist,
                                          date_format='%Y-%m-%d')
print(next_closest_date)
Azat Ibrakov
  • 9,998
  • 9
  • 38
  • 50
  • Also, if I run the function as is without any modification at all (which is my ultimate intent), I get a "TypeError: unorderable types: NoneType() < list()". I am doing something wrong on my end? Thanks! – J. Squillaro Aug 07 '17 at 16:43
  • EDIT: So I found that the issue was the default statement in the function return. Without it, it acts as expected. However, I understand why you used this; is there a way that I can resolve this without having to take out the default statement? – J. Squillaro Aug 07 '17 at 16:51
  • 1
    @J.Squillaro: we can filter `None` values using `filter(None, next_closest_dates_with_none_values)` or `list(filter(None, next_closest_dates_with_none_values))` if you need list (since `filter` creates iterator) – Azat Ibrakov Aug 07 '17 at 19:04