23

So I have a dictionary that looks like this when I print it:

{'10': -10, 'ZT21': 14, 'WX21': 12, '2': 15, '5': -3, 'UM': -25}

I want to sort these in a custom manner, which I define. Let's say the way I want it to be sorted (by key) is ZT21, 10, WX21, UM, 5, 2.

Anyone know how to go about sorting out a dictionary in a predefined/custom manner? What I am doing is getting this dictionary from a database, and it can come out with over 20 keys, all of which have a specific order. The order is always set, but sometimes certain keys/values wouldn't be in the dictionary. So this could happen too:

{'ZT21': 14, 'WX21': 12, '2': 15, '5': -3, 'UM': -25}

sorted (by key) is ZT21, 10, WX21, UM, 5, 2.

So the 10 isn't there in this example, but the sorting I need is still the same, the 10 would just be absent.

Any ideas?

ncica
  • 7,015
  • 1
  • 15
  • 37
user1610719
  • 1,275
  • 2
  • 18
  • 35

5 Answers5

62

Updated answer for Python 3.6+

>>> d = {'10': -10, 'ZT21': 14, 'WX21': 12, '2': 15, '5': -3, 'UM': -25}
>>> keyorder = ['ZT21', '10', 'WX21', 'UM', '5', '2']
>>> {k: d[k] for k in keyorder if k in d}
{'ZT21': 14, '10': -10, 'WX21': 12, 'UM': -25, '5': -3, '2': 15}

Legacy answer: Dictionaries in Python are unordered (before Python3.6). You can get the results you need as a list

>>> d = {'10': -10, 'ZT21': 14, 'WX21': 12, '2': 15, '5': -3, 'UM': -25}
>>> keyorder = ['ZT21', '10', 'WX21', 'UM', '5', '2']
>>> sorted(d.items(), key=lambda i:keyorder.index(i[0]))
[('ZT21', 14), ('10', -10), ('WX21', 12), ('UM', -25), ('5', -3), ('2', 15)]

or as an OrderedDict

>>> from collections import OrderedDict
>>> OrderedDict(sorted(d.items(), key=lambda i:keyorder.index(i[0])))
OrderedDict([('ZT21', 14), ('10', -10), ('WX21', 12), ('UM', -25), ('5', -3), ('2', 15)])

If you are doing a lot of these, it will be more efficient to use a dict for the keyorder

>>> keyorder = {k:v for v,k in enumerate(['ZT21', '10', 'WX21', 'UM', '5', '2'])}
>>> OrderedDict(sorted(d.items(), key=lambda i:keyorder.get(i[0])))
OrderedDict([('ZT21', 14), ('10', -10), ('WX21', 12), ('UM', -25), ('5', -3), ('2', 15)])
John La Rooy
  • 295,403
  • 53
  • 369
  • 502
  • Well I definitely don't mind it not being in a dictionary form, I just want it to go from the dictionary form that comes from the database to a list in that order that I can then print. It seems like your solution is hitting that idea, will answer back if I get it to work asap! – user1610719 Aug 20 '12 at 02:51
  • 2
    Excellent answer! Aside: if you have nested dictionaries -- e.g. `{"0":{"0":{"obj":"book", "rel":"gave", "subj":"Victoria"}}, "1":{"0":{"obj":"it","rel":"loved","subj":"She"}}}` -- you can sort the innermost keys, `keyorder = ['subj', 'rel', 'obj']` as follows: `for i in range(len(dict)): for j in range(len(dict[i])): sorted_dict = sorted(relations_dict[i][j].items(), key=lambda item:keyorder.index(item[0]))` – Victoria Stuart Mar 25 '20 at 23:08
  • how does `.index(i[0])` work? i don't understand how that line sorts the items using the 2nd, 3rd, etc. characters in each string. your answer works and it seems good, but there is no explanation about how or why it works. your first comment is also wrong. as of python 3.6, dictionary keys are ordered. – Anthony Oct 04 '21 at 22:58
  • 1
    @Anthony, each item is a tuple of (string, number), so i[0] refers to the string part of the tuple. Python 3.6 was released 4 years after this answer was written :) – John La Rooy Oct 05 '21 at 22:22
  • @JohnLaRooy thanks for the response. – Anthony Oct 06 '21 at 23:24
4

You can sort it using OrderedDict and by specifying your Custom-order.

def customsort(dict1 , key_order):
    items = [dict1[k] if k in dict1.keys() else 0 for k in key_order] 
    sorted_dict = OrderedDict()
    for i in range(len(key_order)):
        sorted_dict[key_order[i]] = items[i]
    return sorted_dict
key_order = [ "monday" ,"tuesday" ,"wednesday" ,"thursday" ,"friday" ,"saturday"]
dict1 ={"monday" : 10 , "thursday" :12 , "wednesday" : 34}
sorted_dicti = customsort(dict1,key_order)
print(sorted_dicti)

customsort() sorts given dictionary(dict1) by order(key_order) passed by user.

items = [dict1[k] if k in dict1.keys() else 0 for k in key_order] 

It will check if the given key is in dict1 if it is there then put value specified in dict1 else put value as 0.

OrderedDict([('monday', 10), ('tuesday', 0), ('wednesday', 34), ('thursday', 12), ('friday', 0), ('saturday', 0)])
2

You can't. Use a collections.OrderedDict instead.

Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
1

Dictionaries are inherently unordered, so you can't directly sort the dictionary. You can sort the key/value pairs by sorting someDict.items() and passing in a key function just like you would when sorting anything else, but you will get a sorted list and not a dictionary. See previous questions on dictionary sorting: Python: sorting a dictionary of lists and Dictionary sorting by key length for instance.

Community
  • 1
  • 1
BrenBarn
  • 242,874
  • 37
  • 412
  • 384
0

I had exactly the same problem and devised a lightweight general solution:

from collections import OrderedDict

def make_custom_sort(orders):
    orders = [{k: -i for (i, k) in enumerate(reversed(order), 1)} for order in orders]
    def process(stuff):
        if isinstance(stuff, dict):
            l = [(k, process(v)) for (k, v) in stuff.items()]
            keys = set(stuff)
            for order in orders:
                if keys.issuperset(order):
                    return OrderedDict(sorted(l, key=lambda x: order.get(x[0], 0)))
            return OrderedDict(sorted(l))
        if isinstance(stuff, list):
            return [process(x) for x in stuff]
        return stuff
    return process

First, you create an instance of a custom-order sorting function:

custom_sort = make_custom_sort([ ['ZT21', '10', 'WX21', 'UM', '5', '2'] ])

Now, the actual sorting:

result = custom_sort(my_dataset)

The missing keys are rejected at the end in an unspecified order. Note that this closure is recursive. As indicated by the double brackets, you could specify as many sort orders as the various dictionaries nested in your structure would require.

Project on GitHub: https://github.com/laowantong/customsort

Aristide
  • 3,606
  • 2
  • 30
  • 50
  • Is "order" a sequence of items, or something a customer requests? :-) – Tom Russell Mar 30 '18 at 04:03
  • As far as I remember, it's a list of list of keys sorted as the customer wants them to be sorted in any sub-dictionary passed later to `custom_sort`. – Aristide Mar 30 '18 at 08:16