How to custom-sort a list of dict to use in json.dumps How to custom-sort a list of dict to use in json.dumps json json

How to custom-sort a list of dict to use in json.dumps


Since python dicts are unordered collections, use collections.OrderedDict with a custom sort:

from collections import OrderedDictimport jsonallsites = [    {        'A5': 'G',        'A10': 'G',        'site': 'example1.com',        'A1': 'G'    },    {        'A5': 'R',        'A10': 'Y',        'site': 'example2.com',        'A1': 'G'    }]sort_order = ['site', 'A1', 'A5', 'A10']allsites_ordered = [OrderedDict(sorted(item.iteritems(), key=lambda (k, v): sort_order.index(k)))                    for item in allsites]data = {'Author': "joe", 'data': allsites_ordered}print json.dumps(data, indent=4, separators=(',', ': '))

prints:

{    "data": [        {            "site": "example1.com",            "A1": "G",            "A5": "G",            "A10": "G"        },        {            "site": "example2.com",            "A1": "G",            "A5": "R",            "A10": "Y"        }    ],    "Author": "joe"}


In Python3, alecxe's answer no longer works. This should be a comment, but I lack the reputation.

PEP 3113 removed tuple unpacking in function signatures, so the line

allsites_ordered = [OrderedDict(sorted(item.iteritems(), key=lambda (k, v): sort_order.index(k)))                    for item in allsites]

now has to be

allsites_ordered = [OrderedDict(sorted(item.items(), key=lambda item: sort_order.index(item[0])))                    for item in allsites]

or similar. iteritems has also become just items.


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

from collections import OrderedDictdef 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([ ["site", "A1", "A5", "A10"] ])

Now, the actual sorting:

result = custom_sort(allsites)

... which you may dump as a JSON object:

print json.dumps(result, indent=4)

Result

[    {        "site": "example1.com",         "A1": "G",         "A5": "G",         "A10": "G"    },     {        "site": "example2.com",         "A1": "G",         "A5": "R",         "A10": "Y"    }]

More

The 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