辞書から空の文字列のキーを削除する効率的な方法


116

私には辞書があり、空の値の文字列があるすべてのキーを削除したいと思います。

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

これを行う最良の方法は何ですか?

回答:


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}

すべてのキーに値があることに注意してください。これらの値の一部が空の文字列であるだけです。値のないdictにはキーのようなものはありません。それが値を持っていなかった場合、それは口述にはありません。


29
+1。これは実際には既存の辞書からキーを削除するわけではないことに注意することが重要です。むしろ、新しい辞書を作成します。通常、これは正確に誰かが望んでいるものであり、おそらくOPが必要とするものですが、OPが要求したものではありません。
Steven Rumbalski 2012

18
これにより、v = 0も強制終了されます。これが必要な場合は問題ありません。
ポール

2
これはv = Falseも取り除きます。これは、OPが要求したものとは正確には一致しません。
アミール

4
@shredding:つまり.items()
BrenBarn 2016年

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

1
{k:v for k、v in metadata.items()if v!= None}は空の文字列を削除しません。
philgo20 2015年

1
辞書内包表記は、以前のバージョンとの互換性のためにPython 2.7以降でのみサポートされています。@ BrenBarnのソリューションを使用してください。
Pavan Gupta

12
「!=」ではなく、「なし」と「is not」を常に比較する必要があります。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
あなたが交換する必要が3+のPythonを使用している場合.iteritems().items()、最初に最新のPythonのバージョンではもう動作しません。
マリアーノ・ルイス


12

ネストされていることが多く、サイクルを含むことさえある実世界のデータ構造を処理するためのフル機能を備えた簡潔なアプローチが必要な場合は、ボルトンユーティリティパッケージのリマップユーティリティを確認することをお勧めします

iterutils.pyをプロジェクトにpip install boltonsコピーした後、次のようにします。

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
この解決策は、私が抱えていた同様の問題に非常に役立ちました。辞書内の深くネストされたリストから空の値を取り除くことです。ありがとう!
Nicholas Tulach

1
ホイールを再発明せず、ネストされたオブジェクトのソリューションを提供するので、これは良いことです。ありがとう!
vekerdyb

1
あなたがあなたのライブラリーのために書いた記事は本当に気に入りました、そしてこれは便利なライブラリーです!
ライフロガー2018年

11

ライアンの解決策に基づいて、リストとネストされた辞書もある場合:

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": "" }] }
ライアン・シェイ

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

Python 3のitems()代わりに使用iteritems()
andydavies

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 list-comprehension構文を使用して、「空の」値を削除する

落とし穴

  • example01は、元のディクショナリのコピーのみを操作します(その場で変更されません)
  • example01は、「空」という開発者の意味によっては、予期しない結果をもたらす可能性があります。
    • 開発者は偽の値を保持するつもりですか?
    • ディクショナリの値が文字列であることが保証されていない場合、開発者は予期しないデータ損失を起こす可能性があります。
    • result01は、元のセットから3つのキーと値のペアのみが保持されたことを示しています

代替例

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

こちらもご覧ください



4

以下からの回答に基づいて構築patriciasznneonneoあなたが唯一の特定のfalsyのもの(例えば持つキーを削除する場合がありますことを、そして可能性を占める'')が、していない他の(例えば0)、または多分あなたも、いくつかのtruthyものを含めたい(例えば'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

涼しい!この例で機能します。ただし、辞書のアイテムが次の場合は機能しません[]
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. リストcompをforループに展開して効率を上げる
  3. items代わりにpython3 safe を使用してください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
番目

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

これを行う別の方法は、辞書内包表記を使用することです。これはと互換性があります2.7+

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

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での作業用にデスクトップアプリケーションも作成しているため、データ入力アプリケーションで大量のエントリがあり、必須ではないためにユーザーが空白のままにできることを確認しました。検証の目的で、簡単に取得できます。すべてのエントリを削除してから、辞書の空のキーまたは値を破棄します。したがって、上記の私のコードは、辞書の理解を使用して、空白ではない辞書の値要素を保持して、それらを簡単に取り出す方法を示しています。Python3.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

低品質が削除される可能性があるため、回答には現在フラグが付けられています。回答には、コード以外の説明が含まれていることを確認してください。
ティムスタック

@TimStack LQ回答の削除を推奨します。
10担当者

@ 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はニートPythonの約2倍の速さでした。


ビューの反復中にディクショナリを変更することは無効であるため、ループと削除もRunTimeErrorをスローする場合があります。docs.python.org/3/library/stdtypes.html s4.10.1
Airsource Ltd

list(dic.items())ああ男ええええええええええええええええええええええええええええええええええええええええええええええええ、Python 3では真ですが、Python 2.7ではアイテムがリストを返すので、py 3 を呼び出さなければなりません。delは、Null /空の値の比率が低い場合でもより高速に見えます。そのリストを作成することは、dictを再作成することと同じくらいメモリ消費に悪いと思います。
Richard Mathie 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.