user1610719
user1610719

Reputation: 1303

Custom Sorting Python Dictionary

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?

Upvotes: 24

Views: 34060

Answers (5)

John La Rooy
John La Rooy

Reputation: 304147

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)])

Upvotes: 64

Nikita Maru
Nikita Maru

Reputation: 41

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)])

Upvotes: 4

Aristide
Aristide

Reputation: 3984

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

Upvotes: 0

BrenBarn
BrenBarn

Reputation: 251373

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.

Upvotes: 1

Ignacio Vazquez-Abrams
Ignacio Vazquez-Abrams

Reputation: 798616

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

Upvotes: 2

Related Questions