使用递归函数从嵌套字典中删除空字典。

5

我正在尝试从一个嵌套字典中删除非值。 我的第一次尝试效果很好,但不幸的是,指向现在为空的字典的键仍然存在。

因此,如果我执行以下操作:

pass1 = stripper(my_dict)
return stripper(pass1)

这个方法可行,不过我认为可能存在更加优雅且嵌套性更强的解决方案?
def stripper(self, data):
    if isinstance(data, dict):
        d = ({k: stripper(v) for k, v in data.items()
             if v not in [u'', None]})
        if d:
            return d
    else:
        return data

编辑:

失败的例子,下面的字典返回为{'foo': 'bar', 'bar': None}

{
    'foo': 'bar',
    'bar': {
        'foo': None,
        'one': None
    }
}

6
通常情况下你不会把 my_dict 交给脱衣舞女……这样做的结果通常不会太好。 - Joran Beasley
你能否提供一个非常简单的例子,说明这种方法不起作用的情况吗?如果在第一次遍历时留下了空字典,那么后续的遍历也可能会留下空值键... - mgilson
4个回答

11
字典推导式确实很简洁,但如果你将其展开,解决方案会更加明显:
def stripper(self, data):
    new_data = {}
    for k, v in data.items():
        if isinstance(v, dict):
            v = stripper(v)
        if not v in (u'', None, {}):
            new_data[k] = v
    return new_data

not v in ('', None, {}) 这一行中缺少 {} - 我之前没有测试过。我添加了它,我认为它可以工作。谁给我点了踩的,请再看一下,因为我相信这个方法是可行的。 - Oliver Dain
Flake8 提醒我说,倒数第三行应该是 if v not in (u'', None, {}): - chrisinmtown

4

为了扩展接受的答案,使其包括可能是列表元素的对象:

def strip_empties_from_list(data):
    new_data = []
    for v in data:
        if isinstance(v, dict):
            v = strip_empties_from_dict(v)
        elif isinstance(v, list):
            v = strip_empties_from_list(v)
        if v not in (None, str(), list(), dict(),):
            new_data.append(v)
    return new_data


def strip_empties_from_dict(data):
    new_data = {}
    for k, v in data.items():
        if isinstance(v, dict):
            v = strip_empties_from_dict(v)
        elif isinstance(v, list):
            v = strip_empties_from_list(v)
        if v not in (None, str(), list(), dict(),):
            new_data[k] = v
    return new_data

使用方法:

data = {
  'None': None,
  'empty_list': [],
  'empty_dict': {},
  'empty_string': '',
  'list_with_empties': ['', {}, [], None, {'more_empties': '', 'and_even_more': [''], 'one_thing_i_care_about': 'hi'}]
}
stripped_data = strip_empties_from_dict(data)
print(stripped_data)

1

@Oliver的回答是正确的,但是进行一些边缘情况的检查不会有坏处。以下是边缘情况的处理方法:(由于队列已满,无法编辑Oliver的答案)

    def dictionary_stripper(data):
        new_data = {}
        
        # Only iterate if the given dict is not None
        if data:
            for k, v in data.items():
                if isinstance(v, dict):
                    v = dictionary_stripper(v)
                
                # ideally it should be not in, second you can also add a empty list if required
                if v not in ("", None, {}, []):
                    new_data[k] = v
            
            # Only if you want the root dict to be None if empty
            if new_data == {}:
                return None
            return new_data
        return None

CampaignAdminUtils? - chrisinmtown
@chrisinmtown 感谢您指出这一点。我不小心留下了类名,理想情况下应该是递归,所以它应该只是 dictionary_stripper - Sumit Badsara

0

我来到这里,需要去除None值,但保留空字典和列表。重用/修改了@OliverDain的答案,感谢那位。希望这对某人有所帮助。

def drop_none_values(data):
    """
    Recursively remove None values in a dictionary or list;
    this includes list of dicts, etc. Do not drop empty
    dicts or lists.

    :param data: Object to process for None values.
        Returns a new dict if passed a dict;
        returns a new list if passed a list;
        returns the argument unchanged otherwise.
    """
    if isinstance(data, dict):
        new_data = {}
        for k, v in data.items():
            v = drop_none_values(v)
            if v is not None:
                new_data[k] = v
    elif isinstance(data, list):
        new_data = []
        for v in data:
            v = drop_none_values(v)
            if v is not None:
                new_data.append(v)
    else:
        new_data = data
    return new_data

这是我的测试用例,上面的函数在一个名为util的文件/模块中:
def test_drop_none_values():
    data = {
        'first': None,
        'second': None,
        'empty': [],
        'alist': ['string', None, {'foo': None, 'bar': 'baz'}]
    }
    assert len(data) == 4
    stripped = util.drop_none_values(data)
    assert len(stripped) == 2
    assert len(stripped['alist']) == 2
    assert len(stripped['alist'][1]) == 1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接