Разделить список на подсписки на основе разделения строк - PullRequest
0 голосов
/ 14 мая 2018

У меня есть такой список:

a = [['cat1.subcat1.item1', 0], ['cat1.subcat1.item2', 'hello], [cat1.subcat2.item1, 1337], [cat2.item1, 'test']]

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

Я хочу, чтобы список выглядел так:

a = [['cat1', [
        ['subcat1', [
            ['item1', 0],
            ['item2', 'hello']
        ]],
        ['subcat2', [
            ['item1', 1337]
        ]],
    ]],
    ['cat2', [
        ['item1', 'test']
    ]]
]

Надеюсь, это имеет смысл.

В конце мне нужна строка json из этого.Если это проще, его также можно напрямую преобразовать в строку json.

Есть идеи, как этого добиться?Спасибо!

Ответы [ 2 ]

0 голосов
/ 14 мая 2018

Не так красиво, как @jpp их решение, но, эй, , по крайней мере, я пытался . Использование функции merge для объединения глубоких диктовок, как видно из этого ответа .

def merge(a, b, path=None):
    "merges b into a"
    if path is None: path = []
    for key in b:
        if key in a:
            if isinstance(a[key], dict) and isinstance(b[key], dict):
                merge(a[key], b[key], path + [str(key)])
            elif a[key] == b[key]:
                pass # same leaf value
            else:
                raise Exception('Conflict at %s' % '.'.join(path + [str(key)]))
        else:
            a[key] = b[key]
    return a


a = [['cat1.subcat1.item1', 0], ['cat1.subcat1.item2', 'hello'], ['cat1.subcat2.item1', 1337], ['cat2.item1', 'test']]

# convert to dict
b = {x[0]:x[1] for x in a}
res = {}

# iterate over dict
for k, v in list(b.items()):
  s = k.split('.')
  temp = {}
  # iterate over reverse indices,
  # build temp dict from the ground up
  for i in reversed(range(len(s))):
    if i == len(s)-1:
      temp = {s[i]: v}
    else:
      temp = {s[i]: temp}

    # merge temp dict with main dict b
    if i == 0:
      res  = merge(res, temp)
      temp = {}

print(res)
# {'cat1': {'subcat1': {'item1': 0, 'item2': 'hello'}, 'subcat2': {'item1': 1337}}, 'cat2': {'item1': 'test'}}
0 голосов
/ 14 мая 2018

Вы должны использовать вложенную словарную структуру.Это может быть эффективно обработано с использованием collections.defaultdict и functools.reduce.

Преобразование в обычный словарь возможно, хотя обычно и не обязательно.

Решение

from collections import defaultdict
from functools import reduce
from operator import getitem

def getFromDict(dataDict, mapList):
    """Iterate nested dictionary"""
    return reduce(getitem, mapList, dataDict)

tree = lambda: defaultdict(tree)
d = tree()

for i, j in a:
    path = i.split('.')
    getFromDict(d, path[:-1])[path[-1]] = j

Результат

def default_to_regular_dict(d):
    """Convert nested defaultdict to regular dict of dicts."""
    if isinstance(d, defaultdict):
        d = {k: default_to_regular_dict(v) for k, v in d.items()}
    return d

res = default_to_regular_dict(d)

{'cat1': {'subcat1': {'item1': 0,
                      'item2': 'hello'},
          'subcat2': {'item1': 1337}},
 'cat2': {'item1': 'test'}}

Пояснение

  • getFromDict(d, path[:-1]) принимает список path[:-1] и рекурсивно обращается к словарюзначения, соответствующие элементам списка из словаря d.Я реализовал этот бит функционально через functools.reduce и operator.getitem.
  • Затем мы получаем доступ к ключу path[-1], последнему элементу списка, из результирующего дерева словарей.Это будет словарь, так как d является словарём по умолчанию для словарей.Затем мы можем присвоить значение j этому словарю.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...