【问题标题】:Efficient way to remove keys with empty strings from a dict从字典中删除带有空字符串的键的有效方法
【发布时间】:2012-08-20 13:35:52
【问题描述】:

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

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

最好的方法是什么?

【问题讨论】:

    标签: python dictionary


    【解决方案1】:

    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}
    

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

    【讨论】:

    • +1。需要注意的是,这实际上并没有从现有字典中删除键。相反,它创建了一个新字典。通常这正是某人想要的,也可能是 OP 需要的,但这不是 OP 所要求的。
    • 这也会杀死 v=0,这很好,如果需要的话。
    • 这也消除了 v=False,这不是完全 OP 所要求的。
    • @shredding:你的意思是.items()
    • 对于更高版本的python,您还应该使用字典生成器:{k: v for k, v in metadata.items() if v is not None}
    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

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

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

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

      【讨论】:

      • 这也会删除值 0 并且 0 不为空
      • 如果您使用的是 Python 3+,则必须将 .iteritems() 替换为 .items(),第一个在最新的 Python 版本中不再适用。
      【解决方案4】:

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

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

      使用 Python 2.7.3 测试。

      【讨论】:

      • 这也会杀死零值。
      • 要保留 0(零),您可以像这样使用... if v!=None{k: v for k, v in metadata.items() if v!=None}
      • {k: v for k, v in metadata.items() if v!=None} 没有去掉空字符串。
      • 只有 Python 2.7+ 支持字典推导,为了与以前的版本兼容,请使用@BrenBarn 的解决方案。
      • 应始终将 None 与 'is not' 进行比较,而不是 '!='。 stackoverflow.com/a/14247419/2368836
      【解决方案5】:

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

      【讨论】:

      • 在 Python 3 中使用 items() 而不是 iteritems()
      【解决方案6】:

      基于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": "" }] }
      【解决方案7】:

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

      解决方案

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

      陷阱

      • example01 仅对原始字典的副本进行操作(不会就地修改)
      • example01 可能会产生意外结果,具体取决于开发人员所说的“空”的含义
        • 开发人员是否打算保留 falsy 的值?
        • 如果字典中的值不保证为字符串,开发人员可能会出现意外的数据丢失。
        • 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'
        }
      '''
      

      另见

      【讨论】:

        【解决方案8】:

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

        如果您想要一种功能齐全但简洁的方法来处理经常嵌套甚至可以包含循环的实际数据结构,我建议您查看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

        【讨论】:

        • 这个解决方案非常适合我遇到的一个类似问题:从字典内的深度嵌套列表中删除空值。谢谢!
        • 这很好,因为您无需重新发明轮子,而是为嵌套对象提供解决方案。谢谢!
        • 我真的很喜欢你为你的图书馆写的文章,这是一个有用的图书馆!
        • 谢谢你。不要重复道具,而是干净地处理嵌套值。非常好!
        • 这是一个很好的答案,它可以超越这个特定的问题。不过,如果有人发现自己试图回答这个问题,他们可能会遇到类似的问题,这些问题可以通过这个 remap 函数来解决。
        【解决方案10】:

        对于python 3

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

        【讨论】:

          【解决方案11】:

          一些基准测试:

          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.htmls4.10.1
          • 啊,是的,在 python 3 中可以,但在 python 2.7 中不是这样,因为 items 返回一个列表,所以你必须在 py 3 中调用 list(dic.items())。然后是字典理解?对于低比率的空值/空值,del 似乎仍然更快。我想建立这个列表对内存消耗的影响与仅仅重新创建字典一样糟糕。
          【解决方案12】:

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

          result = {
              key: value for key, value in
              {"foo": "bar", "lorem": None}.items()
              if value
          }
          

          【讨论】:

            【解决方案13】:

            我阅读了这个帖子中的所有回复,有些回复也提到了这个帖子: 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
            

            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
            

            【讨论】:

            • 除非我是盲人,否则在我看来尝试 2 和 3 完全一样...
            【解决方案14】:

            如果您使用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_)
            

            【讨论】:

              【解决方案15】:

              上面提到的一些方法会忽略是否有任何整数和浮点值 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
              

              【讨论】:

                【解决方案16】:

                字典与数组混合

                • BlissRage's answerAttempt 3: 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
                

                【讨论】:

                • 太棒了。 . .我在代码库中进行了此更改,但错过了您的评论_/_
                • 这是一个很好的答案,但仍然留下了空数组,以修复更改 if not v in (u'', None, {}, []): 以包含一个空列表
                • @SeanD,刚刚添加了您的建议!!!感谢您为此做出贡献!
                【解决方案17】:

                “由于我目前还为我的 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版本是否也支持最新版本?
                • 您的回答目前被标记为低质量的可能会被删除。请确保您的答案包含除任何代码之外的解释。
                • @TimStack 请建议删除 LQ 答案。
                • @10Rep 对于可能作为解决方案但缺少任何描述性 cmets 的答案,我不建议删除。我宁愿通知用户并教他们什么是更好的答案。
                • @HasseB Mir 我用的是最新的Python 3.8.3
                【解决方案18】:

                要保留 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
                

                【讨论】:

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

                  【讨论】:

                    猜你喜欢
                    • 2022-03-07
                    • 2011-11-15
                    • 2011-01-11
                    • 2010-11-10
                    • 2019-09-17
                    • 2015-08-16
                    • 2015-11-11
                    • 1970-01-01
                    相关资源
                    最近更新 更多