# Complex sorting of nested data

I am retrieving data from Postgres (jsonb type) and I need to return an OrderedDict that has a predictable order for human and machine consumption. There are some common(ish) keys that should be used to direct precedence of values of common types (based on a predefined order and if sort_order is defined). Otherwise, sort order should fall back to key based lexicographic ordering.

The general intent is to have a predictable, 'sane', represenation of composite dicts.

The basic algorithm is:

1. Dicts come before lists
2. Values that are NOT iterables or mapping take precedence over objects that are.
3. Values of the same type whose keys are not in sort_order are considered equal and should be sorted lexicographically.
4. Obj A takes precedence over Obj B if type(A[0]) == type(B) and [0] in sort_order not B[0] in sort_order
5. if all([type(A[1]) == type(B[1]), A[0] in sort_order, B[0] in sort_order]) then the index position of the object key is the precedence determinant.

I have attempted several implementations, but I have not been able to come up with anything that I would consider pythonic/elegant.

Here is the latest incarnation:

# -*- coding: utf-8 -*-

import json
from collections import OrderedDict

def dict_sort(obj, sort_order=None):
def seq(s, o=None, v=None):
return str(s) + str(o) + str(v) if o is not None else str(s)

order_seq = None
if sort_order is not None and obj[0] in sort_order:
order_seq = [i for i, v in enumerate(sort_order) if v == obj[0]][0]

if isinstance(obj[1], dict):
return seq(2, order_seq, obj[0]) if order_seq else seq(3)
elif isinstance(obj[1], list):
return seq(4, order_seq, obj[0]) if order_seq else seq(5)
else:
return seq(0, order_seq, obj[0]) if order_seq else seq(1)

def comp_sort(obj, sort_order=None):
data = OrderedDict()
if isinstance(obj, dict):
for key, value in sorted(obj.items(), key=lambda d: dict_sort(d, sort_order)):
if isinstance(value, dict) or isinstance(value, list):
data[key] = comp_sort(value, sort_order)
else:
data[key] = value
elif isinstance(obj, list):
try:
return sorted(obj)
except:
items = []
for value in obj:
if isinstance(value, dict) or isinstance(value, list):
items.append(comp_sort(value, sort_order))
else:
items.append(value)
return items
return data

# thx herk


Here is a sample data set.

You can use a list comprehension to avoid verbosity:

        items = []
for value in obj:
if isinstance(value, dict) or isinstance(value, list):
items.append(comp_sort(value, sort_order))
else:
items.append(value)
return items


Becomes:

is_dict_or_list = isinstance(value, dict) or isinstance(value, list)
return [comp_sort(value, sort_order) if is_dict_or_list else value
for value in obj ]


Remember to specify:

except ExpectedError:


And not

except:


The latter will swallow anything and harden debugging.