how to sort a list of strigns with months and years in python

Viewed 35

I have the following list:

dates = ['august_2020.xlsb',
 'april_2020.xlsb',
 'december_2020_part1.xlsb',
 'january_2021.xlsb',
 'february_2021.xlsb',
 'may_2020.xlsb',
 'september_2020_part2.xlsb',
 'november_2020_part2.xlsb',
 'june_2020.xlsb',
 'july_2020.xlsb',
 'january_2020.xlsb',
 'december_2020_part2.xlsb',
 'november_2020_part1.xlsb',
 'october_2020_part2.xlsb',
 'february_2020.xlsb',
 'march_2020.xlsb',
 'october_2020_part1.xlsb',
 'september_2020_part1.xlsb']

What I want to do is to sort these strings by months and years such as my final list should look like this:

dates = ['january_2020.xlsb',
 'february_2020.xlsb',
 'march_2020.xlsb',
 'april_2020.xlsb',
 'may_2020.xlsb',
 'june_2020.xlsb',
 'july_2020.xlsb',
 'august_2020.xlsb',
 'september_2020_part1.xlsb',
 'september_2020_part2.xlsb',
 'october_2020_part1.xlsb',
 'october_2020_part2.xlsb',
 'november_2020_part1.xlsb',
 'november_2020_part2.xlsb',
 'december_2020_part1.xlsb',
 'december_2020_part2.xlsb',
 'january_2021.xlsb',
 'february_2021.xlsb'
]

I've tried using: dates.sort() but this is what I'm getting:

dates = ['april_2020.xlsb',
 'august_2020.xlsb',
 'december_2020_part1.xlsb',
 'december_2020_part2.xlsb',
 'february_2020.xlsb',
 'february_2021.xlsb',
 'january_2020.xlsb',
 'january_2021.xlsb',
 'july_2020.xlsb',
 'june_2020.xlsb',
 'march_2020.xlsb',
 'may_2020.xlsb',
 'november_2020_part1.xlsb',
 'november_2020_part2.xlsb',
 'october_2020_part1.xlsb',
 'october_2020_part2.xlsb',
 'september_2020_part1.xlsb',
 'september_2020_part2.xlsb']

I've also tried using this code:

from calendar import month_name
month_lookup = [x.lower() for x in list(month_name)]
sorted(dates, key = month_lookup.index)

However I'm getting this error: ''april_2020.xlsb' is not in list

None of my solutions seems to be working. If anyone knows how to get the desired output I would really appreciate your help.

2 Answers

To sort using date system, you may convert to datetime objects.

  • to match both december_2020_part2.xlsb and february_2020.xlsb you need to split on both dot and underscore, then takes first 2 parts to convert to datetime

  • You may return the datetime AND the filename (so as a tuple), so when dates are same, it uses the filename (that contains part1 or part2) and sort on that

def filename_sorter(filename: str):
    parts = re.split("[._]", filename)
    return datetime.strptime(" ".join(parts[:2]), "%B %Y"), filename

dates = sorted(dates, key=filename_sorter)

Could also be done, by returning a tuple of 3 values : year, month, filename

def filename_sorter(filename: str):
    month, year, *rest = re.split("[._]", filename)
    return int(year), datetime.strptime(month, "%B"), filename

Here is a solution through calendar you used:

import re
from calendar import month_name

month_names = list(month_name)

def month_year_sorter(entry):
    month, year = re.search(r"^(.+?)_(\d{4})", entry).groups()
    return int(year), month_names.index(month.capitalize()), entry

sorted(dates, key=month_year_sorter)

We extract the month and year via a regex and then prioritize sorting on year first, month next (capitalize the first letter and then look-up) and lastly falling back to lexicographic order, by returning a 3-tuple.

Related