Python:如何从NESTED数据结构(列表和词典)中删除无值?

Too*_*eve 13 python recursion dictionary list python-2.7

这是一些嵌套数据,包括列表,元组和字典:

data1 = ( 501, (None, 999), None, (None), 504 )
data2 = { 1:601, 2:None, None:603, 'four':'sixty' }
data3 = OrderedDict( [(None, 401), (12, 402), (13, None), (14, data2)] )
data = [ [None, 22, tuple([None]), (None,None), None], ( (None, 202), {None:301, 32:302, 33:data1}, data3 ) ]
Run Code Online (Sandbox Code Playgroud)

目标:删除任何无键的键或值(来自"数据").如果列表或字典包含一个值,那么它本身就是一个列表,元组或字典,然后是RECURSE,以删除NESTED Nones.

期望的输出:

[[22, (), ()], ((202,), {32: 302, 33: (501, (999,), 504)}, OrderedDict([(12, 402), (14, {'four': 'sixty', 1: 601})]))]
Run Code Online (Sandbox Code Playgroud)

或者更可读的是,这里是格式化输出:

StripNones(data)= list:
. [22, (), ()]
. tuple:
. . (202,)
. . {32: 302, 33: (501, (999,), 504)}
. . OrderedDict([(12, 402), (14, {'four': 'sixty', 1: 601})])
Run Code Online (Sandbox Code Playgroud)

我将提出一个可能的答案,因为我还没有找到现有的解决方案.我感谢任何替代方案,或指向预先存在的解决方案.

编辑 我忘了提到这必须在Python 2.7中工作.我现在不能使用Python 3.

虽然IS值得发布的Python 3解决方案,为别人.所以请说明你要回答哪个python.

mgi*_*son 15

如果您可以假设__init__各个子类的方法与典型基类具有相同的签名:

def remove_none(obj):
  if isinstance(obj, (list, tuple, set)):
    return type(obj)(remove_none(x) for x in obj if x is not None)
  elif isinstance(obj, dict):
    return type(obj)((remove_none(k), remove_none(v))
      for k, v in obj.items() if k is not None and v is not None)
  else:
    return obj

from collections import OrderedDict
data1 = ( 501, (None, 999), None, (None), 504 )
data2 = { 1:601, 2:None, None:603, 'four':'sixty' }
data3 = OrderedDict( [(None, 401), (12, 402), (13, None), (14, data2)] )
data = [ [None, 22, tuple([None]), (None,None), None], ( (None, 202), {None:301, 32:302, 33:data1}, data3 ) ]
print remove_none(data)
Run Code Online (Sandbox Code Playgroud)

请注意,这不会有工作defaultdict的例子,因为在defaultdict需要和额外的参数__init__.使它工作defaultdict将需要另一个特殊情况elif(在常规dicts之前).


还要注意我实际构建了对象.我没有修改旧的.如果您不需要支持修改不可变对象,则可以修改旧对象tuple.


Mah*_*emi 11

如果你想要一个全功能但简洁的方法来处理像这样的现实世界嵌套数据结构,甚至处理周期,我建议从boltons实用程序包中查看重映射实用程序.

之后pip install boltons或复制iterutils.py到您的项目,只是做:

from collections import OrderedDict
from boltons.iterutils import remap

data1 = ( 501, (None, 999), None, (None), 504 )
data2 = { 1:601, 2:None, None:603, 'four':'sixty' }
data3 = OrderedDict( [(None, 401), (12, 402), (13, None), (14, data2)] )
data = [ [None, 22, tuple([None]), (None,None), None], ( (None, 202), {None:301, 32:302, 33:data1}, data3 ) ]

drop_none = lambda path, key, value: key is not None and value is not None

cleaned = remap(data, visit=drop_none)

print(cleaned)

# got:
[[22, (), ()], ((202,), {32: 302, 33: (501, (999,), 504)}, OrderedDict([(12, 402), (14, {'four': 'sixty', 1: 601})]))]
Run Code Online (Sandbox Code Playgroud)

此页面包含更多示例,包括使用更大对象的示例(来自Github的API).

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


the*_*eye 6

def stripNone(data):
    if isinstance(data, dict):
        return {k:stripNone(v) for k, v in data.items() if k is not None and v is not None}
    elif isinstance(data, list):
        return [stripNone(item) for item in data if item is not None]
    elif isinstance(data, tuple):
        return tuple(stripNone(item) for item in data if item is not None)
    elif isinstance(data, set):
        return {stripNone(item) for item in data if item is not None}
    else:
        return data
Run Code Online (Sandbox Code Playgroud)

样本运行:

print stripNone(data1)
print stripNone(data2)
print stripNone(data3)
print stripNone(data)

(501, (999,), 504)
{'four': 'sixty', 1: 601}
{12: 402, 14: {'four': 'sixty', 1: 601}}
[[22, (), ()], ((202,), {32: 302, 33: (501, (999,), 504)}, {12: 402, 14: {'four': 'sixty', 1: 601}})]
Run Code Online (Sandbox Code Playgroud)

  • 如果`data is not None`检查失败,你将从函数的末尾掉下来并返回`None` ...这就是`data`,所以你不妨使用`else`. (2认同)