基于元组值的嵌套字典的元组列表

List of tuples to nested dictionary based on tuple's values

给定一个元组列表,例如

[(1, 'Japan', 1, 'Tokyo'), (1, 'Japan', 2, 'Osaka'), (2, 'Korea', 1, 'Seoul',), (2, 'Korea', 2, 'Pyongyang')]
# country_id, country_name, city_id, city_name

我希望将其结构化为:

{
  'countries': [
    {
      'country_id': 1,
      'country_name': 'Japan',
      'cities': [
        {
          'city_id': 1,
          'city_name': 'Tokyo'
        },
        {
          'city_id': 2,
          'city_name': 'Osaka'
        }
      ]
    },
    {
      'country_id': 2,
      'country_name': 'Korea',
      'cities': [
        {
          'city_id': 1,
          'city_name': 'Seoul'
        },
        {
          'city_id': 2,
          'city_name': 'Pyongyang'
        }
      ]
    }
  ]
}

我实现了它并且运行良好,但不是 Pythonic。想知道这是否可以大大提高 refined/sped,因为这形成了 API 的响应。

x = [(1, 'Japan', 1, 'Tokyo'), (1, 'Japan', 2, 'Osaka'), (2, 'Korea', 1, 'Seoul'), (2, 'Korea', 2, 'Pyongyang')]
countrylist = []
query_countries = []
for a in x:
    if a[0] not in countrylist:
        query_countries.append((a[0], a[1]))
        countrylist.append(a[0])
countrylist = list(set(countrylist))
countries =  [{'country_id': r[0], 'country_name': r[1], 'cities': []} for r in query_countries]
for r in x:
    countries[countrylist.index(r[0])]['cities'].append({'city_id': r[2], 'city_name': r[3]})
final = {'countries': countries}
print(final)
#{'countries': [{'country_id': 1, 'country_name': 'Japan', 'cities': [{'city_id': 1, 'city_name': 'Tokyo'}, {'city_id': 2, 'city_name': 'Osaka'}]}, {'country_id': 2, 'country_name': 'Korea', 'cities': [{'city_id': 1, 'city_name': 'Seoul'}, {'city_id': 2, 'city_name': 'Pyongyang'}]}]}

表达式a[0] not in countrylistcountrylist.index(r[0])不是最有效的,因为countrylist是一个列表,这些操作需要扫描列表。在某些时候你把它变成一个集合,但你可以决定从头到尾都使用一个集合,然后这些查找操作可以在恒定时间内(平均)完成。这已经可以提高大输入的性能。

对于这种挑战,groupbyitemgetter 似乎是很好的工具。他们允许用一个表达式完成工作:

data = [(1, 'Japan', 1, 'Tokyo'), (1, 'Japan', 2, 'Osaka'), (2, 'Korea', 1, 'Seoul',), (2, 'Korea', 2, 'Pyongyang')]

from itertools import groupby
from operator import itemgetter

result = [
    {
        "country_id": country_id,
        "country_name": country_name,
        "cities": [
            {
                "city_id": city_id,
                "city_name": city_name
            } for *_, city_id, city_name in cities
        ]
    } for (country_id, country_name), cities in groupby(data, itemgetter(0, 1))
]

print(result)

groupby

你可以想象这个 groupby 调用 return 这个结构:

[
    (1, 'Japan'), [
        (1, 'Japan', 1, 'Tokyo'), 
        (1, 'Japan', 2, 'Osaka')
    ],
    (2, 'Korea'), [
        (2, 'Korea', 1, 'Seoul'),
        (2, 'Korea', 2, 'Pyongyang')
    ]
]

...除了列表不是列表而是迭代器。但是对于 for ... in 没有区别的语法。

内部元组只是对原始数据的引用,而外层(组)中的值由 itemgetter 生成,它生成前两个值的元组。