从字典中删除带有空字符串的键的有效方法


116

我有一个字典,想删除所有有空值字符串的键。

metadata = {u'Composite:PreviewImage': u'(Binary data 101973 bytes)',
            u'EXIF:CFAPattern2': u''}

做这个的最好方式是什么?

Answers:


194

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 is not None}

请注意,您所有的键都有值。只是其中一些值是空字符串。没有值的字典中就没有键。如果它没有价值,就不会在字典中。


29
+1。重要的是要注意,这实际上并不会从现有字典中删除键。而是,它创建了一个新的字典。通常,这正是某人想要的,可能是OP所需要的,但这不是OP所要求的。
史蒂文·鲁姆巴尔斯基

18
如果需要的话,这也会杀死v = 0,这很好。
保罗

2
这也摆脱了v = False,这与OP要求的不完全相同
阿米尔(Amir)

4
@shredding:你的意思是.items()
布伦·巴恩(BrenBarn)'16

6
对于更高版本的python,您还应该使用字典生成器:{k: v for k, v in metadata.items() if v is not None}
Schiavini

75

它甚至比BrenBarn的解决方案还短(我认为它更具可读性)

{k: v for k, v in metadata.items() if v}

使用Python 2.7.3测试。


13
这也会杀死零值。
保罗

10
要保留0(零),您可以... if v!=None像这样使用: {k: v for k, v in metadata.items() if v!=None}
Dannid 2015年

1
{k:v表示k,v表示meta.items()中的v,如果v!= None}不会消除空字符串。
philgo20

1
字典推导仅受python 2.7+支持,以与以前的版本兼容,请使用@BrenBarn的解决方案。
2015年

12
应始终将“无”与“不是”而不是“!=”进行比较。stackoverflow.com/a/14247419/2368836
rocktheartsm4l

21

如果您确实需要修改原始词典:

empty_keys = [k for k,v in metadata.iteritems() if not v]
for k in empty_keys:
    del metadata[k]

请注意,我们必须列出一个空键,因为我们无法在遍历字典时修改字典(您可能已经注意到)。但是,这(在内存方面)比创建全新的字典便宜(除非存在大量具有空值的条目)。


这也将删除值0并且0不为空
JVK

2
如果您使用的是Python 3+,则必须替换.iteritems().items(),第一个在最新的Python版本中不再起作用。
马里亚诺·鲁伊斯


12

如果您想要一种功能全面但简洁的方法来处理通常是嵌套的甚至可能包含循环的现实世界数据结构,建议您从boltons实用程序包中查看remap实用程序

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

from boltons.iterutils import remap

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

该页面上有更多示例,包括使用Github API处理更大对象的示例。

它是纯Python,因此可在任何地方使用,并已在Python 2.7和3.3+中进行了全面测试。最棒的是,我是针对这种情况编写的,因此,如果您发现它无法处理的情况,可以在这里麻烦我进行修复。


1
这个解决方案对于我遇到的类似问题非常有用:从字典中深层嵌套的列表中删除空值。谢谢!
尼古拉斯·塔拉赫

1
这样做很好,因为您不会重新发明轮子,而是提供嵌套对象的解决方案。谢谢!
vekerdyb

1
我真的很喜欢您为您的图书馆写的文章,这是一个有用的图书馆!
生命记录器'18 -10-13

11

基于Ryan的解决方案,如果您还有列表和嵌套字典:

对于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

1
哈,很好的扩展!对于像这样的字典来说,这是一个很好的解决方案:d = { "things": [{ "name": "" }] }
Ryan Shea 2014年

6

如果您有一个嵌套的字典,并且希望它甚至对空的子元素也适用,则可以使用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()Python 3
andydavies 18/09/24

6

快速解答(TL; DR)

范例01

### 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'}
'''

详细答案

问题

  • 内容: Python 2.x
  • 场景:开发人员希望修改字典以排除空白值
    • aka从字典中删除空值
    • 也就是删除具有空白值的键
    • aka过滤器字典,用于每个键值对上的非空白值

  • example01使用带有简单条件的python list-comprehension语法删除“空”值

陷阱

  • example01仅对原始词典的副本进行操作(未就地修改)
  • example01可能会产生意外结果,具体取决于开发人员“空”的含义
    • 开发人员是否打算保留虚假的价值
    • 如果字典中的值不保证是字符串,则开发人员可能会意外丢失数据。
    • result01显示原始集合中仅保留了三个键值对

替代示例

  • example02帮助解决潜在的陷阱
  • 该方法是通过更改条件使用“空”的更精确定义。
  • 在这里,我们只想滤除评估为空字符串的值。
  • 在这里,我们还使用.strip()过滤出仅包含空格的值。

示例02

### 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'
  }
'''

也可以看看



4

patriciasznneonneo的答案为基础,并考虑到您可能希望删除仅包含某些虚假内容(例如'')但没有其他虚假内容(例如)的密钥的可能性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])}

拍摄得非常好,它可以立即解决许多问题,并且可以解决问题,谢谢您说清楚
jlandercy 16/04/29

凉!它适用于此示例。但是,当词典中的项目为[]
jsga

2

我阅读了该线程中的所有答复,并且也引用了该线程: 使用递归函数删除嵌套字典中的空字典

我最初在这里使用解决方案,效果很好:

尝试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世界中提出了一些性能和兼容性问题:

  1. isinstance代替type
  2. 将列表组合展开到for循环中以提高效率
  3. 使用python3安全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

1
除非我是盲人,否则在我看来尝试2和3完全相同...
luckyguy73

1

带数组的字典

  • 在答案尝试3:刚刚好(到目前为止)BlissRage的回答不能正确处理数组中的元素。我会附上一个补丁,以防有人需要。该方法使用带有的语句块处理列表,该语句块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

棒极了。。。我已经在代码库中进行了此更改,但错过了您的评论_ / _
BlissRage


0

如果您使用的是以下选项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.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

0

“由于我目前还为使用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'}

请提及python版本还支持最新版本吗?
HaseeB Mir

您的答案当前标记为质量差,可能会被删除。请确保您的答案除任何代码外还包含解释。
Tim Stack

@TimStack请建议删除LQ答案。
10 Rep

@ 10Rep我不建议删除可能是解决方案但仅缺少描述性注释的答案。我宁愿通知用户并教他们更好的答案。
蒂姆·斯塔克

@HasseB Mir我使用最新的Python 3.8.3
KokoEfraim

-2

一些基准测试:

1.列表理解重新创建字典

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

2.列表理解使用dict()重新创建dict

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

3.如果v为None,则循环并删除密钥

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的两倍。


循环和删除还可能引发RunTimeError,因为在迭代视图时修改字典是无效的。docs.python.org/3/library/stdtypes.html s4.10.1
Airsource Ltd

嗯,是的,在python 3中是正确的,但在python 2.7中不是,因为项返回一个列表,所以您必须list(dic.items())在py 3中调用。对于低的Null /空值比率,del似乎仍然更快。我想建立该列表与重新创建字典一样,对内存消耗同样有害。
理查德·马蒂
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.