My implementation of a dictionary & list deepSort() recursive function:
*Edit: Keys within dictionaries (and values within lists) are not 'sorted' (alphabetically & numerically ascending) but does appear to organize keys/values of two UDTs in the same order. Hmmm....
def deepSort(obj, ignoreKeys=[]): # Beta
if isinstance(obj, list):
result = []
for value in sorted(obj):
if hasattr(value, '__iter__'):
result.append(deepSort(value))
else:
result.append(value)
return result
elif isinstance(obj, dict):
result = {}
for key, value in sorted(obj.items()):
if not key in ignoreKeys:
if hasattr(value, '__iter__'):
result[key] = deepSort(value)
else:
result[key] = value
return result
else: return sorted(obj)
I wrote this to compare two tag configs (a 'model' UDT vs 'subject' UDT) that exist in two providers. The 'path' parameters were always causing a diff, so I added the ability to ignore specific keys, though decided not to utilize in favor of a less-than-elegant search & replace on a string representation of the config... Actual usage (from script console) resembled:
relTagPath = "_types_/myUDT"
modelProvider = "Provider1"
subjProvider = "Provider2"
ignoreKeys = [] # ['path']
modelTagPath = "[" + modelProvider + "]" + relTagPath
modelCfg = system.tag.getConfiguration(basePath=modelTagPath, recursive=True)[0]
sortedModelCfg = deepSort(modelCfg, ignoreKeys)
subjTagPath = "[" + subjProvider + "]" + relTagPath
subjCfg = system.tag.getConfiguration(basePath=subjTagPath, recursive=True)[0]
sortedSubjCfg = deepSort(subjCfg, ignoreKeys)
replaceString = "[########]" + relTagPath
strSortedModel = str(sortedModelCfg).replace(modelTagPath, replaceString)
strSortedSubj = str(sortedSubjCfg).replace(subjTagPath, replaceString)
print strSortedModel
print strSortedSubj
print (strSortedModel == strSortedSubj)
Still not sure it solves @nminchin's problem...of easily detecting what is different - but appears to recursively sort iterables on the few objects I've tested with.