未拼合的嵌套Python字典

时间:2018-09-17 17:49:22

标签: python list dictionary

将其转换为最干净的方法是什么

Integer variable = 42;
Long other = true? variable: 0L;

进入

{"a.b.c[0].key1": 1, "a.b.c[1].key2": 2, "a.b.c[3].key3": 3}
  • 字典键可以是任何东西。
  • 列表的长度可能会有所不同。
  • 字典的深度可能会有所不同。
  • 如果列表中缺少值,则该值必须为“无”。
  • 如果重复值,则最后声明的是计数值。

我想到了以下工作示例。

想知道我们是否可以为我们的社区找到更好的解决方案。

{"a": {"b": {"c": [{"key1": 1}, {"key2": 2}, None, {"key3": 3}]}}}

3 个答案:

答案 0 :(得分:1)

您可以使用递归:

d = {"a.b.c[0].key1": 1, "a.b.c[1].key2": 2, "a.b.c[3].key3": 3}
from itertools import groupby
import re
def group_data(data):
  new_results = [[a, [i[1:] for i in b]] for a, b in groupby(sorted(data, key=lambda x:x[0]), key=lambda x:x[0])]
  arrays = [[a, list(b)] for a, b in groupby(sorted(new_results, key=lambda x:x[0].endswith(']')), key=lambda x:x[0].endswith(']'))]
  final_result = {}
  for a, b in arrays:
     if a:
       _chars = [[c, list(d)] for c, d in groupby(sorted(b, key=lambda x:re.findall('^\w+', x[0])[0]), key=lambda x:re.findall('^\w+', x[0])[0])]
       _key = _chars[0][0]
       final_result[_key] = [[int(re.findall('\d+', c)[0]), d[0]] for c, d in _chars[0][-1]]
       _d = dict(final_result[_key])
       final_result[_key] = [group_data([_d[i]]) if i in _d else None for i in range(min(_d), max(_d)+1)]
     else:
        for c, d in b:
           final_result[c] = group_data(d) if all(len(i) >1 for i in d) else d[0][0]
  return final_result

print(group_data([[*a.split('.'), b] for a, b in d.items()]))

输出:

{'a': {'b': {'c': [{'key1': 1}, {'key2': 2}, None, {'key3': 3}]}}}

答案 1 :(得分:0)

递归函数可能会更容易使用并且更优雅。

这部分是伪代码,但是它可以帮助您进行思考。

我还没有测试它,但是我很确定它应该可以工作,只要您没有任何直接属于其他列表元素的列表即可。因此,您可以拥有dict的dict,列表的dict和dict的列表,但列表的列表却没有。

def unflatten(data):
    resultDict = {}
    for e in data:
        insertElement(e.split("."), data[e], resultDict)

    return resultDict


def insertElement(path, value, subDict):
    if (path[0] is of the form "foo[n]"):
        key, index = parseListNotation(path[0])
        if (key not in subDict):
            subDict[key] = []

        if (index >= subDict[key].len()):
            subDict[key].expandUntilThisSize(index)

        if (subDict[key][index] == None):
            subDict[key][index] = {}

        subDict[key][index] = insertElement(path.pop(0), value, subDict[key][index])

    else:
        key = path[0]

        if (path.length == 1):
            subDict[key] = value
        else:
            if (key not in subDict):
                subDict[key] = {}

            subDict[key] = insertElement(path.pop(0), value, subDict[key])

    return subDict;

想法是从内到外构建字典。例如:

对于第一个元素,首先创建字典`

  • {key1: 1}

然后将其分配给新词典的元素

  • {c : [None]}, c[0] = {key1: 1}

然后将该字典分配给新字典中的下一个元素b,例如  -{b: {c : [{key1: 1}]}

在新字典中将结果分配给a  -{a: {b: {c : [{key1: 1}]}}

最后返回完整的字典,用于添加下一个值。


如果您不熟悉递归函数,建议您先练习一些简单的函数,然后编写一个满足您期望的函数,但仅输入字典即可。

仅字典递归函数的一般路径:

给出一个路径,该路径是嵌套字典的属性的列表(如果c不是列表,则在您的示例中为[a, b, c, key1]

Start (path, value):

If there's only item in your path, build a dictionary setting 
  that key to your value, and you're done.

If there's more than one, build a dictionary using the first 
  element as a key, and set the value as the output of Start(path.remove(0), value)

答案 2 :(得分:0)

这是如何获得所需结果的另一种形式。虽然不像我想要的那么漂亮,所以我希望有一种更加优雅的方法。如果您花更多的时间在此上,则可能比实际所需的正则表达式要多得多,并且似乎break处理最终键的方法可能只是表明可以改进循环逻辑以消除这种情况而已手动干预。就是说,希望这对在此处改进您的方法很有帮助。

import re

def unflatten(data):
    results = {}
    list_rgx = re.compile(r'[^\[\]]+\[\d+\]')
    idx_rgx = re.compile(r'\d+(?=\])')
    key_rgx = re.compile(r'[^\[]+')
    for text, value in data.items():
        cur = results
        keys = text.split('.')
        idx = None
        for i, key in enumerate(keys):
            stop = (i == len(keys) - 1)          
            if idx is not None:
                val = value if stop else {}  
                if len(cur) > idx:
                    cur[idx] = {key: val}
                else:
                    for x in range(len(cur), idx + 1):
                        cur.append({key: val}) if x == idx else cur.append(None)         
                if stop:
                    break
                else:
                    cur[idx].get(key)
                    idx = None
            if stop:
                cur[key] = value
                break
            elif re.match(list_rgx, key):
                idx = int(re.search(idx_rgx, key).group())
                key = re.search(key_rgx, key).group()
                cur.setdefault(key, [])
            else:
                cur.setdefault(key, {})
            cur = cur.get(key)
    print(results)

输出:

d = {"a.b.c[0].key1": 1, "a.b.c[1].key2": 2, "a.b.c[3].key3": 3}
unflatten(d)

# {'a': {'b': {'c': [{'key1': 1}, {'key2': 2}, None, {'key3': 3}]}}}