0

I have a dictionary that stores countries and offices I need to sort them by Country then by office locations. I manage to sort them by Country by doing this;

sorted_list = [[id, desc] for id, desc in sorted(locations.items(), key=lambda item: (item[1]['name']))

however when I tried to do items[1]['cities']['names'] it didnt work because obviously index is string not integer.

(TypeError: list indices must be integers or slices, not str )

What I need to do is Sort By Country first and Sort By cities inside

Expected Output:

Austria -> Gramatneusiedl / Vienna, Home Office, Lenzing, Schoerfling, Weissenstein

Belgium -> Antwerp, Brussels, Home Office, Oostende

Initial Input:

 locations = '2248': {
            'name': 'Austria',
            'cities': [{
                'OptionId': 2289,
                'name': 'Gramatneusiedl / Vienna'
            }, {
                'OptionId': 2290,
                'name': 'Lenzing'
            }, {
                'OptionId': 2291,
                'name': 'Schoerfling'
            }, {
                'OptionId': 2292,
                'name': 'Weissenstein'
            }, {
                'OptionId': 2293,
                'name': 'Home Office'
            }]
        },

    '2249': {
                'name': 'Belgium',
                'cities': [{
                    'OptionId': 9367,
                    'name': 'Oostende'
                }, {
                    'OptionId': 2294,
                    'name': 'Antwerp'
                }, {
                    'OptionId': 2295,
                    'name': 'Brussels'
                }, {
                    'OptionId': 2296,
                    'name': 'Home Office'
                }]
Anar Bayramov
  • 11,158
  • 5
  • 44
  • 64

2 Answers2

1

Dictionary keys in Python do not guarantee order. You can see this SO post for more details: Python dictionary, how to keep keys/values in same order as declared?. There are ways to do it, and depending on what version of Python you are using, it might be different.

If you just want to print the items in order, there is an easy way to manage it using the json library.

import json
ordered = {v['name']: sorted([i['name'] for i in v['cities']]) for v in locations.values()}
json.dumps(ordered, sort_keys=True)

This will print the following result:

{
  "Austria": [
    "Gramatneusiedl / Vienna",
    "Home Office",
    "Lenzing",
    "Schoerfling",
    "Weissenstein"
  ],
  "Belgium": [
    "Antwerp",
    "Brussels",
    "Home Office",
    "Oostende"
  ]
}

If you want to maintain order within the dictionary, then use an OrderedDict object. OrderedDicts remember insertion order. So we can sort all of the items based on the value of the name key before creating the final dictionary.

from collections import OrderedDict
ordered = OrderedDict()
# sort each dictionary by their name field
sorted_by_country = sorted(locations.values(), key = lambda x:x['name'])
for v in sorted_by_country:
    ordered[v['name']] = sorted([i['name'] for i in v['cities']])
TheF1rstPancake
  • 2,318
  • 17
  • 17
  • as of 3.7 dictionaries are guaranteed to be insertion order. – ShpielMeister Dec 26 '17 at 21:39
  • Right. I think it's actually as of 3.6, and that's discussed in the other post I linked to. If you are worried about what version of Python someone may be using to run your code, it makes sense to cover your bases with `OrderedDict`. – TheF1rstPancake Dec 26 '17 at 21:41
  • insertion ordered in 3.6 only for CPython and not guaranteed: https://stackoverflow.com/questions/47965481/python-3-6-collections-document-mistake-found/47965522#47965522 https://mail.python.org/pipermail/python-dev/2017-December/151283.html – ShpielMeister Dec 26 '17 at 22:14
0

You can try this:

locations = {'2248': {'cities': [{'OptionId': 2289, 'name': 'Gramatneusiedl / Vienna'}, {'OptionId': 2290, 'name': 'Lenzing'}, {'OptionId': 2291, 'name': 'Schoerfling'}, {'OptionId': 2292, 'name': 'Weissenstein'}, {'OptionId': 2293, 'name': 'Home Office'}], 'name': 'Austria'}, '2249': {'cities': [{'OptionId': 9367, 'name': 'Oostende'}, {'OptionId': 2294, 'name': 'Antwerp'}, {'OptionId': 2295, 'name': 'Brussels'}, {'OptionId': 2296, 'name': 'Home Office'}], 'name': 'Belgium'}}
new_locations = {a:{c:d if isinstance(d, str) else sorted(d, key=lambda x:x['name']) for c, d in b.items()} for a, b in locations.items()}

Output:

{'2248': {'cities': [{'OptionId': 2289, 'name': 'Gramatneusiedl / Vienna'}, {'OptionId': 2293, 'name': 'Home Office'}, {'OptionId': 2290, 'name': 'Lenzing'}, {'OptionId': 2291, 'name': 'Schoerfling'}, {'OptionId': 2292, 'name': 'Weissenstein'}], 'name': 'Austria'}, '2249': {'cities': [{'OptionId': 2294, 'name': 'Antwerp'}, {'OptionId': 2295, 'name': 'Brussels'}, {'OptionId': 2296, 'name': 'Home Office'}, {'OptionId': 9367, 'name': 'Oostende'}], 'name': 'Belgium'}}

Edit: Python dictionaries are inherently unsorted, however, you can use a collections.OrderedDict to compensate:

from collections import OrderedDict
new_locations = OrderedDict()
final_data = sorted(locations.items(), key=lambda x:x[-1]['name'])
for a, b in final_data:
   new_locations[a] = {'name':b['name'], 'cities':sorted(b['cities'], key=lambda x:x['name'])}

Output:

OrderedDict([('2248', {'cities': [{'OptionId': 2289, 'name': 'Gramatneusiedl / Vienna'}, {'OptionId': 2293, 'name': 'Home Office'}, {'OptionId': 2290, 'name': 'Lenzing'}, {'OptionId': 2291, 'name': 'Schoerfling'}, {'OptionId': 2292, 'name': 'Weissenstein'}], 'name': 'Austria'}), ('2249', {'cities': [{'OptionId': 2294, 'name': 'Antwerp'}, {'OptionId': 2295, 'name': 'Brussels'}, {'OptionId': 2296, 'name': 'Home Office'}, {'OptionId': 9367, 'name': 'Oostende'}], 'name': 'Belgium'})])
Ajax1234
  • 69,937
  • 8
  • 61
  • 102