Python:如何删除嵌套dict中的所有空字段

时间:2015-01-15 22:13:55

标签: python

如果我有一个字典,哪个字段的值也可能是字典或数组。如何删除其中的所有空字段?

“空字段”表示字段的值为空数组([]),无或空字典(所有子字段均为空)。

实施例: 输入:

{
    "fruit": [
        {"apple": 1},
        {"banana": None}
    ],
    "veg": [],
    "result": {
        "apple": 1,
        "banana": None
    }
}

输出:

{
    "fruit": [
        {"apple": 1}
    ],
    "result": {
        "apple": 1
    }
}

4 个答案:

答案 0 :(得分:19)

使用返回新字典的递归函数:

def clean_empty(d):
    if not isinstance(d, (dict, list)):
        return d
    if isinstance(d, list):
        return [v for v in (clean_empty(v) for v in d) if v]
    return {k: v for k, v in ((k, clean_empty(v)) for k, v in d.items()) if v}

{..}构造是词典理解;如果v true ,它只会包含原始词典中的键,例如不是空的。类似地,[..]构造构建一个列表。

嵌套的(.. for ..)构造是生成器表达式,允许代码在递归后紧凑地过滤空对象

请注意,任何设置为 numeric 0 (整数0,浮点数0.0)的值也将被清除。您可以使用if v or v == 0保留数字0值。

演示:

>>> sample = {
...     "fruit": [
...         {"apple": 1},
...         {"banana": None}
...     ],
...     "veg": [],
...     "result": {
...         "apple": 1,
...         "banana": None
...     }
... }
>>> def clean_empty(d):
...     if not isinstance(d, (dict, list)):
...         return d
...     if isinstance(d, list):
...         return [v for v in (clean_empty(v) for v in d) if v]
...     return {k: v for k, v in ((k, clean_empty(v)) for k, v in d.items()) if v}
... 
>>> clean_empty(sample)
{'fruit': [{'apple': 1}], 'result': {'apple': 1}}

答案 1 :(得分:8)

如果您想要一个功能齐全但简洁的方法来处理经常嵌套的真实数据结构,甚至可以包含循环和其他类型的容器,我建议您查看the remap utility from the boltons utility package

pip install boltons或将iterutils.py复制到项目后,只需执行以下操作:

from boltons.iterutils import remap

data = {'veg': [], 'fruit': [{'apple': 1}, {'banana': None}], 'result': {'apple': 1, 'banana': None}}

drop_falsey = lambda path, key, value: bool(value)
clean = remap(data, visit=drop_falsey)
print(clean)

# Output:
{'fruit': [{'apple': 1}], 'result': {'apple': 1}}

This page还有更多示例,包括使用Github API中更大对象的示例。

它是纯Python,因此它可以在任何地方使用,并且在Python 2.7和3.3+中进行了全面测试。最重要的是,我为这样的情况编写了它,所以如果你找到一个它无法处理的情况,你可以告诉我修复它right here

答案 2 :(得分:0)

@mojoken-如何解决布尔问题

def clean_empty(d):
if not isinstance(d, (dict, list)):
    return d
if isinstance(d, list):
    return [v for v in (clean_empty(v) for v in d) if isinstance(v, bool) or v]
return {k: v for k, v in ((k, clean_empty(v)) for k, v in d.items()) if isinstance(v, bool) or v}

答案 3 :(得分:-2)

def remove_empty_fields(data_):
    """
        Recursively remove all empty fields from a nested
        dict structure. Note, a non-empty field could turn
        into an empty one after its children deleted.

        :param data_: A dict or list.
        :return: Data after cleaning.
    """
    if isinstance(data_, dict):
        for key, value in data_.items():

            # Dive into a deeper level.
            if isinstance(value, dict) or isinstance(value, list):
                value = remove_empty_fields(value)

            # Delete the field if it's empty.
            if value in ["", None, [], {}]:
                del data_[key]

    elif isinstance(data_, list):
        for index in reversed(range(len(data_))):
            value = data_[index]

            # Dive into a deeper level.
            if isinstance(value, dict) or isinstance(value, list):
                value = remove_empty_fields(value)

            # Delete the field if it's empty.
            if value in ["", None, [], {}]:
                data_.pop(index)

    return data_