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:
- Dicts come before lists
- Values that are NOT iterables or mapping take precedence over objects that are.
- Values of the same type whose keys are not in sort_order are considered equal and should be sorted lexicographically.
- Obj A takes precedence over Obj B
if type(A[0]) == type(B) and [0] in sort_order not B[0] in sort_order
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.