【发布时间】:2012-08-20 13:35:52
【问题描述】:
我有一个字典,想删除所有有空值字符串的键。
metadata = {u'Composite:PreviewImage': u'(Binary data 101973 bytes)',
u'EXIF:CFAPattern2': u''}
最好的方法是什么?
【问题讨论】:
标签: python dictionary
我有一个字典,想删除所有有空值字符串的键。
metadata = {u'Composite:PreviewImage': u'(Binary data 101973 bytes)',
u'EXIF:CFAPattern2': u''}
最好的方法是什么?
【问题讨论】:
标签: python dictionary
Python 2.X
dict((k, v) for k, v in metadata.iteritems() if v)
Python 2.7 - 3.X
{k: v for k, v in metadata.items() if v}
请注意,您的所有键都有值。只是其中一些值是空字符串。没有值的字典中没有键之类的东西;如果它没有值,它就不会在字典中。
【讨论】:
.items()。
{k: v for k, v in metadata.items() if v is not None}
BrenBarn's solution 是理想的(和pythonic,我可能会补充)。然而,这是另一个 (fp) 解决方案:
from operator import itemgetter
dict(filter(itemgetter(1), metadata.items()))
【讨论】:
如果确实需要修改原词典:
empty_keys = [k for k,v in metadata.iteritems() if not v]
for k in empty_keys:
del metadata[k]
请注意,我们必须创建一个空键列表,因为我们无法在迭代字典时修改它(您可能已经注意到)。但是,这比创建一个全新的字典要便宜(在内存方面),除非有很多具有空值的条目。
【讨论】:
.iteritems() 替换为 .items(),第一个在最新的 Python 版本中不再适用。
【讨论】:
... if v!=None:{k: v for k, v in metadata.items() if v!=None}
如果您有一个嵌套字典,并且您希望它甚至适用于空子元素,您可以使用 BrenBarn 建议的递归变体:
def scrub_dict(d):
if type(d) is dict:
return dict((k, scrub_dict(v)) for k, v in d.iteritems() if v and scrub_dict(v))
else:
return d
【讨论】:
items() 而不是 iteritems()
基于Ryan's solution,如果您还有列表和嵌套字典:
对于 Python 2:
def remove_empty_from_dict(d):
if type(d) is dict:
return dict((k, remove_empty_from_dict(v)) for k, v in d.iteritems() if v and remove_empty_from_dict(v))
elif type(d) is list:
return [remove_empty_from_dict(v) for v in d if v and remove_empty_from_dict(v)]
else:
return d
对于 Python 3:
def remove_empty_from_dict(d):
if type(d) is dict:
return dict((k, remove_empty_from_dict(v)) for k, v in d.items() if v and remove_empty_from_dict(v))
elif type(d) is list:
return [remove_empty_from_dict(v) for v in d if v and remove_empty_from_dict(v)]
else:
return d
【讨论】:
d = { "things": [{ "name": "" }] }
### example01 -------------------
mydict = { "alpha":0,
"bravo":"0",
"charlie":"three",
"delta":[],
"echo":False,
"foxy":"False",
"golf":"",
"hotel":" ",
}
newdict = dict([(vkey, vdata) for vkey, vdata in mydict.iteritems() if(vdata) ])
print newdict
### result01 -------------------
result01 ='''
{'foxy': 'False', 'charlie': 'three', 'bravo': '0'}
'''
### example02 -------------------
mydict = { "alpha":0,
"bravo":"0",
"charlie":"three",
"delta":[],
"echo":False,
"foxy":"False",
"golf":"",
"hotel":" ",
}
newdict = dict([(vkey, vdata) for vkey, vdata in mydict.iteritems() if(str(vdata).strip()) ])
print newdict
### result02 -------------------
result02 ='''
{'alpha': 0,
'bravo': '0',
'charlie': 'three',
'delta': [],
'echo': False,
'foxy': 'False'
}
'''
【讨论】:
基于patriciasz 和nneonneo 的答案,并考虑到您可能希望删除只有某些虚假内容(例如'')而不是其他内容(例如0)的密钥,或者你甚至想要包含一些真实的东西(例如'SPAM'),那么你可以制作一个高度具体的命中列表:
unwanted = ['', u'', None, False, [], 'SPAM']
不幸的是,这并不完全有效,因为例如0 in unwanted 的计算结果为True。我们需要区分0和其他虚假的东西,所以我们必须使用is:
any([0 is i for i in unwanted])
...计算为False。
现在用它来del不需要的东西:
unwanted_keys = [k for k, v in metadata.items() if any([v is i for i in unwanted])]
for k in unwanted_keys: del metadata[k]
如果你想要一个新的字典,而不是在原地修改metadata:
newdict = {k: v for k, v in metadata.items() if not any([v is i for i in unwanted])}
【讨论】:
[] 时,它不起作用
如果您想要一种功能齐全但简洁的方法来处理经常嵌套甚至可以包含循环的实际数据结构,我建议您查看the remap utility from the boltons utility package。
在pip install boltons 或将iterutils.py 复制到您的项目之后,只需执行以下操作:
from boltons.iterutils import remap
drop_falsey = lambda path, key, value: bool(value)
clean = remap(metadata, visit=drop_falsey)
This page 有更多示例,包括处理来自 Github API 的更大对象的示例。
它是纯 Python,因此可以在任何地方使用,并且在 Python 2.7 和 3.3+ 中经过全面测试。最重要的是,我是专门为这样的情况编写的,所以如果你发现它无法处理的情况,你可以让我修复它right here。
【讨论】:
对于python 3
dict((k, v) for k, v in metadata.items() if v)
【讨论】:
In [7]: %%timeit dic = {str(i):i for i in xrange(10)}; dic['10'] = None; dic['5'] = None
...: dic = {k: v for k, v in dic.items() if v is not None}
1000000 loops, best of 7: 375 ns per loop
In [8]: %%timeit dic = {str(i):i for i in xrange(10)}; dic['10'] = None; dic['5'] = None
...: dic = dict((k, v) for k, v in dic.items() if v is not None)
1000000 loops, best of 7: 681 ns per loop
In [10]: %%timeit dic = {str(i):i for i in xrange(10)}; dic['10'] = None; dic['5'] = None
...: for k, v in dic.items():
...: if v is None:
...: del dic[k]
...:
10000000 loops, best of 7: 160 ns per loop
所以循环和删除在 160ns 时最快,列表理解在 ~375ns 时慢一半,而调用 dict() 又是一半慢 ~680ns。
将 3 包装到一个函数中会使它再次下降到大约 275ns。同样对我来说,PyPy 的速度大约是 neet python 的两倍。
【讨论】:
list(dic.items())。然后是字典理解?对于低比率的空值/空值,del 似乎仍然更快。我想建立这个列表对内存消耗的影响与仅仅重新创建字典一样糟糕。
您可以这样做的另一种方法是使用字典理解。这应该兼容2.7+
result = {
key: value for key, value in
{"foo": "bar", "lorem": None}.items()
if value
}
【讨论】:
我阅读了这个帖子中的所有回复,有些回复也提到了这个帖子: Remove empty dicts in nested dictionary with recursive function
我最初在这里使用了解决方案,效果很好:
尝试 1:太热(性能不佳或面向未来):
def scrub_dict(d):
if type(d) is dict:
return dict((k, scrub_dict(v)) for k, v in d.iteritems() if v and scrub_dict(v))
else:
return d
但是在 Python 2.7 世界中提出了一些性能和兼容性问题:
isinstance 而不是type
for 循环中以提高效率items而不是iteritems
尝试 2:太冷(缺乏记忆):
def scrub_dict(d):
new_dict = {}
for k, v in d.items():
if isinstance(v,dict):
v = scrub_dict(v)
if not v in (u'', None, {}):
new_dict[k] = v
return new_dict
DOH!这不是递归的,根本不是记忆。
尝试 3:恰到好处(到目前为止):
def scrub_dict(d):
new_dict = {}
for k, v in d.items():
if isinstance(v,dict):
v = scrub_dict(v)
if not v in (u'', None, {}):
new_dict[k] = v
return new_dict
【讨论】:
如果您使用pandas,这是一个选项:
import pandas as pd
d = dict.fromkeys(['a', 'b', 'c', 'd'])
d['b'] = 'not null'
d['c'] = '' # empty string
print(d)
# convert `dict` to `Series` and replace any blank strings with `None`;
# use the `.dropna()` method and
# then convert back to a `dict`
d_ = pd.Series(d).replace('', None).dropna().to_dict()
print(d_)
【讨论】:
上面提到的一些方法会忽略是否有任何整数和浮点值 0 和 0.0
如果有人想避免上述情况,可以使用以下代码(从嵌套字典和嵌套列表中删除空字符串和 None 值):
def remove_empty_from_dict(d):
if type(d) is dict:
_temp = {}
for k,v in d.items():
if v == None or v == "":
pass
elif type(v) is int or type(v) is float:
_temp[k] = remove_empty_from_dict(v)
elif (v or remove_empty_from_dict(v)):
_temp[k] = remove_empty_from_dict(v)
return _temp
elif type(d) is list:
return [remove_empty_from_dict(v) for v in d if( (str(v).strip() or str(remove_empty_from_dict(v)).strip()) and (v != None or remove_empty_from_dict(v) != None))]
else:
return d
【讨论】:
if isinstance(v, list): 语句块处理列表,它使用原始scrub_dict(d) 实现来清理列表。 @staticmethod
def scrub_dict(d):
new_dict = {}
for k, v in d.items():
if isinstance(v, dict):
v = scrub_dict(v)
if isinstance(v, list):
v = scrub_list(v)
if not v in (u'', None, {}, []):
new_dict[k] = v
return new_dict
@staticmethod
def scrub_list(d):
scrubbed_list = []
for i in d:
if isinstance(i, dict):
i = scrub_dict(i)
scrubbed_list.append(i)
return scrubbed_list
【讨论】:
if not v in (u'', None, {}, []): 以包含一个空列表
“由于我目前还为我的 Python 工作编写了一个桌面应用程序,我发现在数据输入应用程序中有很多条目并且其中一些不是强制性的,因此用户可以将其留空,以进行验证,它是轻松获取所有条目,然后丢弃字典的空键或值。所以我上面的代码展示了我们如何轻松取出它们,使用字典理解并保持字典值元素不为空。我使用 Python 3.8.3
data = {'':'', '20':'', '50':'', '100':'1.1', '200':'1.2'}
dic = {key:value for key,value in data.items() if value != ''}
print(dic)
{'100': '1.1', '200': '1.2'}
【讨论】:
要保留 0 和 False 值,但要删除可以使用的空值:
{k: v for k, v in metadata.items() if v or v == 0 or v is False}
对于具有混合类型值的嵌套字典,您可以使用:
def remove_empty_from_dict(d):
if isinstance(d, dict):
return dict((k, remove_empty_from_dict(v)) for k, v in d.items() \
if v or v == 0 or v is False and remove_empty_from_dict(v) is not None)
elif isinstance(d, list):
return [remove_empty_from_dict(v) for v in d
if v or v == 0 or v is False and remove_empty_from_dict(v) is not None]
else:
if d or d == 0 or d is False:
return d
【讨论】:
metadata ={'src':'1921','dest':'1337','email':'','movile':''}
ot = {k: v for k, v in metadata.items() if v != ''}
print(f"Final {ot}")
【讨论】: