0

I'm trying to create an event manager in which a dictionary stores the events like this

my_dict = {'2020': 
                 {'9': {'8': ['School ']}, 
                  '11': {'13': ['Doctors ']},
                  '8': {'31': ['Interview']}
                 }, 
           '2021': {}}

In which the outer key is the year the middle key is a month and the most inner key is a date which leads to a list of events. I'm trying to first sort it so that the months are in order then sort it again so that the days are in order. Thanks in advance

azro
  • 53,056
  • 7
  • 34
  • 70
  • 2
    Then what operation do you want to apply, iterate over ? – azro Aug 19 '20 at 20:34
  • I was thinking about using sorted with the keys parameter but I couldn't figure it out how to access the inner dictionaries also Im not 100% on sorted so if you have a better idea that would be great – OrangeCrush Aug 19 '20 at 20:43
  • But what is the *purpose* of having it sorted? Dictionaries don't care about order: you use them to find a given key, and it does the job. If order is important, then why not a nested list instead? – trincot Aug 19 '20 at 20:45

4 Answers4

1

Use-case

  • DevOrangeCrush wishes to sort on keys in a nested dictionary where the nesting occurs on multiple levels

Solution

  • Normalize the data so that the dates match ISO8601 format, for easier sorting

    • In plain English, this means make sure you always use two digits for month and date, and always use four digits for year
  • Re-normalize the original dictionary data structure into a single list of dictionaries, where each dictionary represents a row, and the list represents an outer containing table

  • Once your data is restructured you are solving a much more well-known, well-documented, and more obvious problem, how to sort a simple list of dictionaries (which is already documented in the See also section of this answer).

Example

import pprint

## original data is formatted as a nested dictionary, which is clumsy

my_dict = {'2020': 
   {'9': {'8': ['School ']}, '11': 
   {'13': ['Doctors ']},'8': 
   {'31': ['Interview']}}, '2021': {}
}

## we want the data formatted as a standard table (aka list of dictionary)
## this is the most common format for this kind of data as you would see in
## databases and spreadsheets

mydata_table  = []
ddtemp        = dict()
for year in my_dict:
  for month in my_dict[year].keys():
    ddtemp['month'] =   '{0:02d}'.format(*[int(month)])
    ddtemp['year']  =   year
    for day in my_dict[year][month].keys():
      ddtemp['day'] = '{0:02d}'.format(*[int(day)])
      mydata_row = dict()
      mydata_row['year']        =   '{year}'.format(**ddtemp)
      mydata_row['month']       =   '{month}'.format(**ddtemp)
      mydata_row['day']         =   '{day}'.format(**ddtemp)
      mydata_row['task_list']   =   my_dict[year][month][day]
      mydata_row['date']        =   '{year}-{month}-{day}'.format(**ddtemp)
      mydata_table.append(mydata_row)
    pass
  pass
pass

## output result is now easily sorted and there is no data loss
## you will have to modify this if you want to deal with years that
## do not have any associated task_list data

pprint.pprint(mydata_table)

'''

## now we have something that can be sorted using well-known python idioms
## and easily manipulated using data-table semantics 
## (search, sort, filter-by, group-by, select, project ... etc)

[
  {'date': '2020-09-08','day': '08',
    'month': '09','task_list': ['School '],'year': '2020'},
  {'date': '2020-11-13','day': '13',
    'month': '11','task_list': ['Doctors '],'year': '2020'},
  {'date': '2020-08-31','day': '31',
    'month': '08','task_list': ['Interview'],'year': '2020'},
]

'''

See also

dreftymac
  • 31,404
  • 26
  • 119
  • 182
  • @OrangeCrush pprint dumps a large variable in an easily-readable format, you can use it anytime you have a big dictionary or any arbitrary variable in python, and you want to see what is inside of it [pprint](https://stackoverflow.com/tags/pprint/info) [pretty-print](https://stackoverflow.com/questions/tagged/pretty-print) – dreftymac Aug 19 '20 at 21:18
1

To get sorted events data, you can do something like this:

def sort_events(my_dict):
    new_events_data = dict()
    for year, month_data in my_dict.items():
        new_month_data = dict()
        for month, day_data in month_data.items():
            sorted_day_data = sorted(day_data.items(), key=lambda kv: int(kv[0]))
            new_month_data[month] = OrderedDict(sorted_day_data)
        sorted_months_data = sorted(new_month_data.items(), key=lambda kv: int(kv[0]))
        new_events_data[year] = OrderedDict(sorted_months_data)
    return new_events_data

Output:

{'2020': OrderedDict([('8', OrderedDict([('31', ['Interview'])])),
              ('9', OrderedDict([('8', ['School '])])),
              ('11', OrderedDict([('13', ['Doctors '])]))]),
 '2021': OrderedDict()}
0

A simple dict can't be ordered, you could do it using a OrderedDict but if you simply need to get it sorted while iterating on it do like this

for year in sorted(map(int, my_dict)):
    year_dict = my_dict[str(year)]

    for month in sorted(map(int, year_dict)):
        month_dict = year_dict[str(month)]

        for day in sorted(map(int, month_dict)):
            events = month_dict[str(day)]

            for event in events:
                print(year, month, day, event)

Online Demo

The conversion to int is to ensure right ordering between the numbers, without you'll get 1, 10, 11, .., 2, 20, 21

azro
  • 53,056
  • 7
  • 34
  • 70
-2

A dictionary in Python does not have an order, you might want to try the OrderedDict class from the collections Module which remembers the order of insertion.

Of course you would have to sort and reinsert the elements whenever you insert a new element which should be placed before any of the existing elements.

If you care about order maybe a different data structure works better. For example a list of lists.

Oli4
  • 24
  • 4