23

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.

Community
  • 1
  • 1
Andy
  • 49,085
  • 60
  • 166
  • 233

3 Answers3

40

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'})
RocketDonkey
  • 36,383
  • 7
  • 80
  • 84
  • 1
    `key=lambda (k,v): v['position']` is a bit easier on the eyes +1 – Jon Clements Nov 26 '12 at 21:14
  • @JonClements Can't argue with that :) Changed. – RocketDonkey Nov 26 '12 at 21:16
  • 31
    Note that in python 3, `key=lambda(k,v): v['position']` must be written as `key=lambda k_v: k_v[1]['position']` – dano Jun 19 '14 at 17:42
  • 11
    And `dict.iteritems()` is in python3 `dict.items()`: https://stackoverflow.com/questions/30418481/error-dict-object-has-no-attribute-iteritems-when-trying-to-use-networkx – Jeppe Mar 20 '18 at 15:35
  • 5
    Both of you are right. Just tested successfully in *python3* `sorted(statuses.items(), key=lambda k_v: k_v[1]['position'])` – gies0r Aug 31 '18 at 09:51
11
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'})]
Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
1

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.

BR19_so
  • 71
  • 3
  • 1
    Adding to this answer, if the keys of your parent dictionary are strings as opposed to integers, then you would want to use this line when rebuilding your dictionary in the right order: dictOfDicts = {k: dictOfDicts[k] for k in newIndex} This is because you can't slice lists on strings (the newIndex variable is a list). – jjasper Jun 28 '22 at 01:52