Питонический способ преобразования / сглаживания JSON, содержащего вложенные структуры table-as-list-of-dicts

Предположим, у меня есть таблица, представленная в JSON в виде списка dicts, где ключи каждого элемента одинаковы:

      J = [
    {
        "symbol": "ETHBTC",
        "name": "Ethereum",
        :
    },
    {
        "symbol": "LTC",
        "name": "LiteCoin"
        :
    },

И предположим, мне нужен эффективный поиск, например symbols['ETHBTC']['name']

Я могу трансформироваться с symbols = { item['name']: item for item in J }, производящие:

      {
    "ETHBTC": {
        "symbol": "ETHBTC",
        "name": "Ethereum",
        :
    },
    "LTCBTC": {
        "symbol": "LTCBTC",
        "name": "LiteCoin",
        :
    },

(В идеале я бы также удалил теперь лишний symbol поле).

Однако что, если каждый элемент сам по себе содержит «таблицу как список слов»?

Вот более полный минимальный пример (я удалил строки, не относящиеся к проблеме):

      J = {
    "symbols": [
        {
            "symbol":"ETHBTC",
            "filters":[
                {
                    "filterType":"PRICE_FILTER",
                    "minPrice":"0.00000100",
                },
                {
                    "filterType":"PERCENT_PRICE",
                    "multiplierUp":"5",
                },
            ],
        },
        {
            "symbol":"LTCBTC",
            "filters":[
                {
                    "filterType":"PRICE_FILTER",
                    "minPrice":"0.00000100",
                },
                {
                    "filterType":"PERCENT_PRICE",
                    "multiplierUp":"5",
                },
            ],
        }
    ]
}

Итак, задача состоит в том, чтобы преобразовать эту структуру в:

      J = {
    "symbols": {
        "ETHBTC": {
            "filters": {
                "PRICE_FILTER": {
                    "minPrice": "0.00000100",
    :
}

Я могу написать flatten функция:

      def flatten(L:list, key) -> dict:
    def remove_key_from(D):
        del D[key]
        return D
    return { D[key]: remove_key_from(D)  for D in L }

Затем я могу сгладить внешний список и перебрать каждый ключ / значение в полученном слове, сглаживая val['filters']:

      J['symbols'] = flatten(J['symbols'], key="symbol")
for symbol, D in J['symbols'].items():
    D['filters'] = flatten(D['filters'], key="filterType")

Можно ли улучшить это, используя glom (или иным образом)?

Начальное преобразование не имеет ограничений производительности, но мне нужен эффективный поиск.

2 ответа

Поскольку у вас разные правила преобразования для разных ключей, вы можете вести список имен ключей, требующих «группировки»:

      t = ['symbol', 'filterType']
def transform(d):
   if (m:={a:b for a, b in d.items() if a in t}):
      return {[*m.values()][0]:transform({a:b for a, b in d.items() if a not in m})}
   return {a:b if not isinstance(b, list) else {x:y for j in b for x, y in transform(j).items()} for a, b in d.items()}

      import json
print(json.dumps(transform(J), indent=4))
      {
    "symbols": {
        "ETHBTC": {
            "filters": {
                "PRICE_FILTER": {
                    "minPrice": "0.00000100"
                },
                "PERCENT_PRICE": {
                    "multiplierUp": "5"
                }
            }
        },
        "LTCBTC": {
            "filters": {
                "PRICE_FILTER": {
                    "minPrice": "0.00000100"
                },
                "PERCENT_PRICE": {
                    "multiplierUp": "5"
                }
            }
        }
    }
}

Я не знаю, назовете ли вы это pythonic, но вы можете сделать свою функцию более универсальной, используя рекурсию и отбрасывая ключ в качестве аргумента. Поскольку вы уже предполагаете, что ваши списки содержат словари, вы можете извлечь выгоду из динамической типизации Python, сделав любой ввод:

      from pprint import pprint
def flatten_rec(I) -> dict:
    if isinstance(I, dict):
        I = {k: flatten_rec(v) for k,v in I.items()}
    elif isinstance(I, list):
        I = { list(D.values())[0]: {k:flatten_rec(v) for k,v in list(D.items())[1:]} for D in I }
    return I

pprint(flatten_rec(J))

Выход:

      {'symbols': {'ETHBTC': {'filters': {'PERCENT_PRICE': {'multiplierUp': '5'},
                                    'PRICE_FILTER': {'minPrice': '0.00000100'}}},
             'LTCBTC': {'filters': {'PERCENT_PRICE': {'multiplierUp': '5'},
                                    'PRICE_FILTER': {'minPrice': '0.00000100'}}}}}
Другие вопросы по тегам