根据字符串拆分将列表拆分为子列表

时间:2018-05-14 11:02:47

标签: python json python-3.x list dictionary

我有一个这样的清单:

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 :(得分:4)

您应该使用嵌套字典结构。这可以使用collections.defaultdictfunctools.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.reduceoperator.getitem功能实现了此功能。
  • 然后,我们从结果字典树中访问列表的最后一个元素path[-1]。这将是一个字典,因为d是字典的默认用法。然后,我们可以将值j分配给此词典。

答案 1 :(得分:1)

不像@jpp他们的解决方案那么漂亮,但嘿至少我试过。使用merge函数合并深层词汇,如this answer中所示。

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'}}