更清洁的方式来解压缩嵌套词典

时间:2017-10-20 08:47:03

标签: python dictionary

我从JSON格式的API批量接收数据。我希望只在列表中存储值。

原始数据看起来像,并且总是如下所示,即:所有{...}看起来像第一个例子

data = content.get('data')
>>> [{'a':1, 'b':{'c':2, 'd':3}, 'e':4}, {...}, {...}, ...]

嵌套字典使这更难;我也需要打开包装。

这就是我所拥有的,但它感觉非常糟糕:

unpacked = []
data = content.get('data')
for d in data:
    item = []
    for k, v in d.items():
        if k == 'b':
            for val in v.values():
                item.append(val)
        else:
            item.append(v)
    unpacked.append(item)

输出:

>>> [[1,2,3,4], [...], [...], ...]

我该如何改善这个?

5 个答案:

答案 0 :(得分:3)

您可以使用递归函数和某些类型测试:

data = [{'a':1, 'b':{'c':2, 'd':3}, 'e':4}, {'f':5,'g':6}]

def extract_nested_values(it):
    if isinstance(it, list):
        for sub_it in it:
            yield from extract_nested_values(sub_it)
    elif isinstance(it, dict):
        for value in it.values():
            yield from extract_nested_values(value)
    else:
        yield it

print(list(extract_nested_values(data)))
# [1, 2, 3, 4, 5, 6]

请注意,它会输出一个平面生成器,而不是列表列表。

答案 1 :(得分:2)

假设您的词典不包含内部列表,您可以定义一个简单的例程来解压缩嵌套字典,并使用循环遍历数据中的每个项目。

def unpack(data):
    for k, v in data.items():
        if isinstance(v, dict):
            yield from unpack(v)
        else:
            yield v

请注意,由于yield from的魔力,此功能非常简单。现在,让我们用一些数据来称呼它。

data = [{'a':1, 'b':{'c':2, 'd':3}, 'e':4}, {'f':5,'g':6}]  # Data "borrowed" from Kaushik NP
result = [list(unpack(x)) for x in data]

print(result)
[[2, 3, 1, 4], [5, 6]]

请注意结果中缺少顺序,因为字典的任意顺序。

答案 2 :(得分:1)

出于完整性考虑,基于埃里克·杜米尼尔(Eric Duminil)的出色回答,以下函数可返回嵌套字典或列表的最大深度:

def depth(it, count=0):
    """Depth of a nested dict.
    # Arguments
        it: a nested dict or list.
        count: a constant value used in internal calculations.
    # Returns
        Numeric value.
    """
    if isinstance(it, list):
        if any(isinstance(v, list) or isinstance(v, dict) for v in it):
            for v in it:
                if isinstance(v, list) or isinstance(v, dict):
                    return depth(v, count + 1)
        else:
            return count
    elif isinstance(it, dict):
        if any(isinstance(v, list) or isinstance(v, dict) for v in it.values()):
            for v in it.values():
                if isinstance(v, list) or isinstance(v, dict):
                    return depth(v, count + 1)
        else:
            return count
    else:
        return count

在Python传统中,它是从零开始的。

答案 3 :(得分:0)

递归地执行:

def traverse(d): 
    for key,val in d.items(): 
        if isinstance(val, dict): 
             traverse(val) 
        else: 
             l.append(val) 

out=[]
for d in data:
    l=[]
    traverse(d)
    out.append(l)

print(out)

#driver values:

IN : data = [{'a':1, 'b':{'c':2, 'd':3}, 'e':4}, {'f':5,'g':6}]
OUT : out = [[1, 2, 3, 4], [5, 6]]

编辑:更好的方法是使用yield,以便不必像第一种方法那样依赖全局变量。

def traverse(d): 
    for key,val in d.items(): 
        if isinstance(val, dict): 
             yield from traverse(val) 
        else: 
             yield val

out = [list(traverse(d)) for d in data]

答案 4 :(得分:0)

其他答案(特别是@COLDSPEED)已经涵盖了这种情况,但是这里有一个略有不同的代码,基于旧格言最好是请求宽恕而非许可,我倾向于选择检查:

def unpack(data):
    try:
        for value in data.values():
            yield from unpack(value)
    except AttributeError:
        yield data


data = [{'a':1, 'b':{'c':2, 'd':3}, 'e':4}]
unpacked = [list(unpack(item)) for item in data]