137

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

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

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

4

19 回答 19

227

蟒蛇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}

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

于 2012-08-25T02:36:42.863 回答
80

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

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

使用 Python 2.7.3 测试。

于 2014-01-31T14:07:19.483 回答
22

如果确实需要修改原字典:

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

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

于 2012-08-25T03:09:03.010 回答
17

如果您想要一种功能齐全但简洁的方法来处理经常嵌套甚至可以包含循环的实际数据结构,我建议您查看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+ 中经过全面测试。最重要的是,我是为这样的情况写的,所以如果你发现它不能处理的情况,你可以让我在这里修复它。

于 2016-02-08T05:09:47.353 回答
15

根据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
于 2014-07-22T16:51:59.367 回答
13

BrenBarn 的解决方案是理想的(我可能会补充说 Pythonic)。但是,这是另一个(fp)解决方案:

from operator import itemgetter
dict(filter(itemgetter(1), metadata.items()))
于 2012-08-25T03:00:14.600 回答
8

如果您有一个嵌套字典,并且您希望它甚至适用于空子元素,您可以使用 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
于 2014-07-11T01:16:45.567 回答
7

对于蟒蛇 3

dict((k, v) for k, v in metadata.items() if v)
于 2017-01-30T14:28:06.370 回答
6

快速回答 (TL;DR)

Example01

### 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
  • 场景:开发人员希望修改字典以排除空白值
    • 又名从字典中删除空值
    • 又名删除具有空白值的键
    • 又名过滤字典,用于每个键值对上的非空值

解决方案

  • example01 使用带有简单条件的 python 列表理解语法来删除“空”值

陷阱

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

替代示例

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

Example02

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

也可以看看

于 2015-10-31T19:29:37.567 回答
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])}
于 2015-11-16T14:55:22.447 回答
2

我阅读了这个线程中的所有回复,有些回复也提到了这个线程: 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 世界中提出了一些性能和兼容性问题:

  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

哦!这不是递归的,根本不是记忆。

尝试 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
于 2018-03-26T18:12:33.913 回答
1

字典与数组混合

  • 尝试 3的答案: BlissRage 的答案中的Just Right(到目前为止)没有正确处理数组元素。我包括一个补丁,以防有人需要它。该方法是带有语句块的句柄列表,它使用原始实现擦洗列表。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
于 2020-01-29T01:53:08.710 回答
1

“由于我目前还为我的 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'}
于 2020-07-22T10:04:25.897 回答
1

要保留 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
于 2021-03-18T15:15:51.907 回答
0

您可以这样做的另一种方法是使用字典理解。这应该兼容2.7+

result = {
    key: value for key, value in
    {"foo": "bar", "lorem": None}.items()
    if value
}
于 2018-02-26T19:09:21.937 回答
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_)
于 2018-07-20T21:55:04.253 回答
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
于 2019-02-07T16:02:47.887 回答
0
metadata ={'src':'1921','dest':'1337','email':'','movile':''}
ot = {k: v for k, v in metadata.items() if v != ''}
print(f"Final {ot}")
于 2021-04-23T23:27:34.730 回答
-2

一些基准测试:

1.列表理解重新创建dict

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()一半,调用 to 又是 ~680ns 慢一半。

将 3 包装到一个函数中会使它再次下降到大约 275ns。同样对我来说,PyPy 的速度大约是 neet python 的两倍。

于 2017-03-09T16:56:59.200 回答