'Sort dictionary of dictionaries by value

I have this dictionary:

statuses = {
            'pending' : {'status_for':'all', 'position':1},
            'cancelled' : {'status_for':'all','position':2},
            'approved' : {'status_for':'owner', 'position':1},
            'rejected - owner' : {'status_for':'owner', 'position':2},
            'accepted' : {'status_for':'dev', 'position':1},
            'rejected - developer' : {'status_for':'dev', 'position':3},
            'closed' : {'status_for':'dev', 'position':5},
            }

I've also got a function to pull all status_for values of either owner or dev that looks like this and put it into a PyQt QComboBox:

for s in statuses:
            if statuses[s]['status_for'] == "dev" or statuses[s]['status_for'] == "all":
                cb_developer_status.addItem(s.capitalize(), s)   

I'd like to order these by the position value though. What is a good way to do this, so that when I populate by combobox I have it in a predefined order?

I realize that the snippet above is checking for both 'dev' and 'all', my assumption right now is that I'd have to loop through the dictionary twice to get the two separate blocks in the order I wish (ie. 'all' appears before 'dev').

I saw this post, but I'm not sure how to convert this answer to something that is a dictionary of dictionaries.



Solution 1:[1]

Would something like this work? Similar to the post you linked, this uses the key function of sorted to provide a custom sort order. iteritems() returns a (key, value) tuple, so that gets passed into lambda (x, y): y['position'], where y['position'] is the value (your nested dictionary, keyed by the status), and position is the item by which you want to sort.

In [35]: statuses = {
            'pending' : {'status_for':'all', 'position':1},
            'cancelled' : {'status_for':'all','position':2},
            'approved' : {'status_for':'owner', 'position':1},
            'rejected - owner' : {'status_for':'owner', 'position':2},
            'accepted' : {'status_for':'dev', 'position':1},
            'rejected - developer' : {'status_for':'dev', 'position':3},
            'closed' : {'status_for':'dev', 'position':5},
            }

In [44]: for s in sorted(statuses.iteritems(), key=lambda (x, y): y['position']):
   ....:     print s
   ....:
   ....:
('accepted', {'position': 1, 'status_for': 'dev'})
('approved', {'position': 1, 'status_for': 'owner'})
('pending', {'position': 1, 'status_for': 'all'})
('rejected - owner', {'position': 2, 'status_for': 'owner'})
('cancelled', {'position': 2, 'status_for': 'all'})
('rejected - developer', {'position': 3, 'status_for': 'dev'})
('closed', {'position': 5, 'status_for': 'dev'})

Solution 2:[2]

In [232]: statuses = {                                                                  
            'pending' : {'status_for':'all', 'position':1},
            'cancelled' : {'status_for':'all','position':2},
            'approved' : {'status_for':'owner', 'position':1},
            'rejected - owner' : {'status_for':'owner', 'position':2},
            'accepted' : {'status_for':'dev', 'position':1},
            'rejected - developer' : {'status_for':'dev', 'position':3},
            'closed' : {'status_for':'dev', 'position':5},
            }

In [235]: sorted(statuses,key=lambda x:statuses[x]['position'])
Out[235]: 
['accepted',
 'approved',
 'pending',
 'rejected - owner',
 'cancelled',
 'rejected - developer',
 'closed']

or using operator.getitem():

In [260]: from operator import *

In [261]: sorted(statuses.items(),key=lambda x:getitem(x[1],'position'))
Out[261]: 
[('accepted', {'position': 1, 'status_for': 'dev'}),
 ('approved', {'position': 1, 'status_for': 'owner'}),
 ('pending', {'position': 1, 'status_for': 'all'}),
 ('rejected - owner', {'position': 2, 'status_for': 'owner'}),
 ('cancelled', {'position': 2, 'status_for': 'all'}),
 ('rejected - developer', {'position': 3, 'status_for': 'dev'}),
 ('closed', {'position': 5, 'status_for': 'dev'})]

Solution 3:[3]

I know this is old now to answer, but I ran in a similar problem with almost the same dictionary structure you have, and this how I solved it,

I have structure like this:

dictOfDicts = {
   0: {'id': 3, 'title': 'hello'}, 
   1: {'id': 1, 'title': 'hi'},
   2: {'id': 2, 'title': 'aloha'},
}

and I wanted to order it to be like this:

dictOfDicts = {
   0: {'id': 1, 'title': 'hi'}, 
   1: {'id': 2, 'title': 'aloha'}
   2: {'id': 3, 'title': 'hello'},
}

i.e to change the outer items to be ordered by using inner 'id' items.


So I first got the new index in a list for the outer items by using sorted():

newIndex = sorted(dictOfDicts, key=lambda x: dictOfDicts[x]['id'])

now newIndex has the new order in a list

#output
newIndex = [1,2,0]

then I used this to change the order for the outer items using newIndex:

dictOfDicts = {newIndex[k]: dictOfDicts[k] for k in newIndex}

If anyone wants to know what exactly happened just tell me in the comments, I don't want the answer to be longer than necessary since the question got answered already.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 BR19_so