2

假设我有一个用 JSON 表示的表作为字典列表,其中每个项目的键是相同的:

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字段)。

但是,如果每个项目本身都包含一个“table-as-list-of-dicts”怎么办?

这是一个更完整的最小示例(我删除了与问题无关的行):

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是否可以使用(或其他方式)对此进行改进?

初始转换没有性能限制,但我需要有效的查找。

4

2 回答 2

1

由于您对不同的键有不同的转换规则,您可以保留一个需要“分组”的键名列表:

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"
                }
            }
        }
    }
}
于 2021-10-28T15:54:53.873 回答
1

我不知道你是否称它为 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'}}}}}
于 2021-10-28T07:21:13.500 回答