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

时间:2012-08-25 02:35:12

标签: python dictionary

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

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

这样做的最佳方式是什么?

19 个答案:

答案 0 :(得分:156)

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}

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

答案 1 :(得分:69)

它甚至可以比BrenBarn's solution更短(我认为更具可读性)

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

使用Python 2.7.3进行测试。

答案 2 :(得分:20)

如果你真的需要修改原始字典:

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

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

答案 3 :(得分:11)

如果你想要一个全功能但简洁的方法来处理经常嵌套的真实数据结构,甚至可以包含周期,我建议你看看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

答案 4 :(得分:9)

BrenBarn's solution是理想的(和pythonic,我可以补充)。这是另一个(fp)解决方案,但是:

from operator import itemgetter
dict(filter(itemgetter(1), metadata.items()))

答案 5 :(得分:8)

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

答案 6 :(得分: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

答案 7 :(得分:4)

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

解决方案

  • example01使用python list-comprehension语法和简单的条件来删除" empty"值

陷阱

  • example01仅对原始字典的副本进行操作(不进行适当修改)
  • example01可能会产生意想不到的结果,这取决于开发人员的意思是什么"空"
    • 开发人员是否意味着要保留falsy的值?
    • 如果字典中的值不是字符串,开发人员可能会意外丢失数据。
    • 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'
  }
'''

另见

答案 8 :(得分: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])}

答案 9 :(得分:3)

对于python 3

dict((k, v) for k, v in metadata.items() if v)

答案 10 :(得分:1)

我读了这个帖子中的所有回复,有些也提到了这个帖子: 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. 将列表comp展开到for循环以提高效率
  3. 使用python3 safe items代替iteritems
  4. 尝试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
    

答案 11 :(得分:1)

与数组混合的点

  • 来自BlissRage's answer尝试3:恰到好处(到目前为止)的答案不能正确处理数组元素。我会附上一个补丁,以防有人需要。该方法是使用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

答案 12 :(得分:1)

”“由于我目前还为使用Python编写桌面应用程序,因此我在数据输入应用程序中发现有很多条目,而其中一些条目不是强制性的,因此用户可以将其留空,以进行验证,容易获取所有条目,然后丢弃空键或字典的值。因此,我上面的代码演示了如何使用字典理解并轻松保留字典值元素(不为空)将它们取出。我使用Python 3.8.3 < / p>

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

答案 13 :(得分: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

答案 14 :(得分:0)

您可以使用字典理解的另一种方法。这应该与public function categories() { return $this->belongsToMany(Category::class)->withPivot('column1','column2'); }

兼容
2.7+

答案 15 :(得分: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_)

答案 16 :(得分: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

答案 17 :(得分:0)

metadata ={'src':'1921','dest':'1337','email':'','movile':''}
ot = {k: v for k, v in metadata.items() if v != ''}
print(f"Final {ot}")

答案 18 :(得分:-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()的调用再慢一半~680ns。

将3包裹到一个函数中会使其再次回落到约275ns。另外对我来说PyPy的速度是neet python的两倍。