Подтвердить что ты не робот

Как настроить сортировку списка 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 по сортировке, но он просто дает возможность переопределить функцию сравнения, не говоря уже о том, что я не знаете, как вставить это в мой код.

Как это сделать?

4b9b3361

Ответ 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