Как настроить сортировку списка dict для использования в json.dumps
У меня есть список, похожий на
allsites = [
{
'A5': 'G',
'A10': 'G',
'site': 'example1.com',
'A1': 'G'
},
{
'A5': 'R',
'A10': 'Y',
'site': 'example2.com',
'A1': 'G'
}
]
Что я использую в json.dumps
:
data = { 'Author':"joe", 'data':allsites }
print json.dumps(data,sort_keys=True,indent=4, separators=(',', ': '))
Здесь выводится следующий JSON:
{
"Author": "joe",
"data": [
{
"A1": "G",
"A10": "G",
"A5": "G",
"site": "example1.com"
},
{
"A1": "G",
(...)
Я хотел бы, чтобы раздел "данные" этой строки JSON сортировался через пользовательский ключ ( "алфавит" ), в случае выше это было бы site, A1, A5, A10
и на самом деле выглядело бы следующим образом:
{
"Author": "joe",
"data": [
{
"site": "example1.com",
"A1": "G",
"A5": "G",
"A10": "G"
},
{
"site": "example2.com",
"A1": "G",
(...)
Я прочитал о пользовательской сортировке в FAQ по сортировке, но он просто дает возможность переопределить функцию сравнения, не говоря уже о том, что я не знаете, как вставить это в мой код.
Как это сделать?
Ответы
Ответ 1
Так как python dicts - неупорядоченные коллекции, используйте collections.OrderedDict
с помощью специального вида:
from collections import OrderedDict
import json
allsites = [
{
'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=(',', ': '))
печатает:
{
"data": [
{
"site": "example1.com",
"A1": "G",
"A5": "G",
"A10": "G"
},
{
"site": "example2.com",
"A1": "G",
"A5": "R",
"A10": "Y"
}
],
"Author": "joe"
}
Ответ 2
В Python3 ответ alecxe больше не работает. Это должен быть комментарий, но мне не хватает репутации.
PEP 3113 удалил кортеж распаковки в сигнатурах функций, поэтому строка
allsites_ordered = [OrderedDict(sorted(item.iteritems(), key=lambda (k, v): sort_order.index(k)))
for item in allsites]
теперь должно быть
allsites_ordered = [OrderedDict(sorted(item.items(), key=lambda item: sort_order.index(item[0])))
for item in allsites]
или аналогичный. iteritems
также стал просто items
.
Ответ 3
У меня была точно такая же проблема, и я разработал легкое общее решение:
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
Сначала вы создаете экземпляр функции сортировки пользовательского порядка:
custom_sort = make_custom_sort([ ["site", "A1", "A5", "A10"] ])
Теперь, фактическая сортировка:
result = custom_sort(allsites)
... который вы можете сбросить как объект JSON:
print json.dumps(result, indent=4)
Результат
[
{
"site": "example1.com",
"A1": "G",
"A5": "G",
"A10": "G"
},
{
"site": "example2.com",
"A1": "G",
"A5": "R",
"A10": "Y"
}
]
Подробнее
Закрытие рекурсивное. Как указано в двойных скобках, вы можете указать столько заказов сортировки, сколько потребуются различные словари, вложенные в вашу структуру.
Проект на GitHub: https://github.com/laowantong/customsort