Cara yang efisien untuk menghapus kunci dengan string kosong dari sebuah dict


116

Saya memiliki dict dan ingin menghapus semua kunci yang string nilainya kosong.

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

Apa cara terbaik untuk melakukannya?

Jawaban:


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}

Perhatikan bahwa semua kunci Anda memiliki nilai. Hanya saja beberapa dari nilai tersebut adalah string kosong. Tidak ada yang namanya kunci dalam sebuah dict tanpa nilai; jika tidak memiliki nilai, tidak akan ada di dict.


29
+1. Penting untuk dicatat bahwa ini tidak benar-benar menghapus kunci dari kamus yang ada. Sebaliknya, itu membuat kamus baru. Biasanya ini persis seperti yang diinginkan seseorang dan mungkin itulah yang dibutuhkan OP, tetapi bukan itu yang diminta OP.
Steven Rumbalski

18
Ini juga membunuh v = 0, yang bagus, jika itu yang diinginkan.
Paul

2
Ini juga menghapus v = False, yang tidak persis seperti yang diminta OP.
Amir

4
@ pencabikan: Maksud Anda .items().
BrenBarn

6
Untuk versi python yang lebih baru, Anda juga harus menggunakan generator kamus:{k: v for k, v in metadata.items() if v is not None}
Schiavini

75

Ini bisa menjadi lebih pendek dari solusi BrenBarn (dan saya pikir lebih mudah dibaca)

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

Diuji dengan Python 2.7.3.


13
Ini juga membunuh nilai nol.
Paul

10
Untuk mempertahankan 0 (nol) Anda dapat menggunakan ... if v!=Noneseperti ini: {k: v for k, v in metadata.items() if v!=None}
Dannid

1
{k: v untuk k, v dalam metadata.items () if v! = None} tidak menghilangkan string kosong.
philgo20

1
pemahaman kamus hanya didukung dengan Python 2.7+ untuk kompatibilitas dengan versi sebelumnya, gunakan solusi @ BrenBarn.
Pavan Gupta

12
Harus selalu membandingkan Tidak Ada dengan, 'bukan', bukan '! ='. stackoverflow.com/a/14247419/2368836
rocktheartsm4l

21

Jika Anda benar-benar perlu mengubah kamus asli:

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

Perhatikan bahwa kita harus membuat daftar kunci kosong karena kita tidak dapat mengubah kamus saat mengulanginya (seperti yang mungkin telah Anda ketahui). Ini lebih murah (dari segi memori) daripada membuat kamus baru, kecuali jika ada banyak entri dengan nilai kosong.


ini juga akan menghapus nilai 0 dan 0 tidak kosong
JVK

2
Jika Anda menggunakan Python 3 + Anda harus mengganti .iteritems()dengan .items(), pertama tidak bekerja lagi dalam versi Python terbaru.
Mariano Ruiz

12

Solusi BrenBarn ideal (dan pythonic, saya bisa menambahkan). Berikut adalah solusi lain (fp), namun:

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

12

Jika Anda menginginkan pendekatan berfitur lengkap namun ringkas untuk menangani struktur data dunia nyata yang sering bersarang, dan bahkan dapat berisi siklus, saya sarankan untuk melihat utilitas pemetaan ulang dari paket utilitas boltons .

Setelah pip install boltonsatau menyalin iterutils.py ke dalam proyek Anda, lakukan saja:

from boltons.iterutils import remap

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

Halaman ini memiliki lebih banyak contoh, termasuk yang bekerja dengan objek yang jauh lebih besar dari API Github.

Ini murni-Python, jadi berfungsi di mana-mana, dan sepenuhnya diuji dengan Python 2.7 dan 3.3+. Yang terbaik dari semuanya, saya menulisnya persis untuk kasus seperti ini, jadi jika Anda menemukan kasus yang tidak tertangani, Anda dapat mengganggu saya untuk memperbaikinya di sini .


1
Solusi ini bekerja dengan baik untuk masalah serupa yang saya alami: menghapus nilai kosong dari daftar yang sangat bersarang di dalam kamus. Terima kasih!
Nicholas Tulach

1
Ini bagus, karena Anda tidak menciptakan kembali roda, dan memberikan solusi untuk objek bersarang. Terima kasih!
vekerdyb

1
Saya sangat menyukai artikel yang Anda tulis untuk perpustakaan Anda, dan ini adalah perpustakaan yang berguna!
lifelogger

11

Berdasarkan solusi Ryan , jika Anda juga memiliki daftar dan kamus bertingkat:

Untuk 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

Untuk 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
Ha, ekstensi yang bagus! Ini adalah solusi yang baik untuk kamus seperti berikut:d = { "things": [{ "name": "" }] }
Ryan Shea

6

Jika Anda memiliki kamus bertingkat, dan Anda ingin ini berfungsi bahkan untuk sub-elemen kosong, Anda dapat menggunakan varian rekursif dari saran 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

Gunakan items()alih-alih iteritems()untuk Python 3
andydavies

6

Jawaban Cepat (TL; DR)

Contoh01

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

Jawaban Terperinci

Masalah

  • Konteks: Python 2.x
  • Skenario: Pengembang ingin mengubah kamus untuk mengecualikan nilai kosong
    • alias menghapus nilai kosong dari kamus
    • alias hapus kunci dengan nilai kosong
    • kamus filter alias untuk nilai yang tidak kosong di setiap pasangan nilai kunci

Larutan

  • example01 menggunakan sintaks daftar-pemahaman python dengan syarat sederhana untuk menghapus nilai "kosong"

Jebakan

  • example01 hanya beroperasi pada salinan kamus asli (tidak diubah di tempat)
  • example01 dapat memberikan hasil yang tidak diharapkan tergantung pada apa yang dimaksud pengembang dengan "kosong"
    • Apakah pengembang berarti untuk menjaga nilai-nilai yang falsy ?
    • Jika nilai dalam kamus tidak dijamin sebagai string, pengembang mungkin mengalami kehilangan data yang tidak terduga.
    • result01 menunjukkan bahwa hanya tiga pasangan nilai kunci yang dipertahankan dari kumpulan aslinya

Contoh alternatif

  • example02 membantu menangani potensi jebakan
  • Pendekatannya adalah dengan menggunakan definisi yang lebih tepat dari "kosong" dengan mengubah kondisional.
  • Di sini kami hanya ingin memfilter nilai yang dievaluasi menjadi string kosong.
  • Di sini kami juga menggunakan .strip () untuk memfilter nilai yang hanya terdiri dari spasi.

Contoh02

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

Lihat juga



4

Membangun jawaban dari patriciasz dan nneonneo , dan memperhitungkan kemungkinan bahwa Anda mungkin ingin menghapus kunci yang hanya memiliki hal-hal palsu tertentu (misalnya '') tetapi tidak yang lain (misalnya 0), atau mungkin Anda bahkan ingin memasukkan beberapa hal yang benar (misalnya 'SPAM') , lalu Anda dapat membuat daftar target yang sangat spesifik:

unwanted = ['', u'', None, False, [], 'SPAM']

Sayangnya, ini tidak cukup berhasil, karena misalnya 0 in unwantedmengevaluasi ke True. Kita perlu membedakan antara 0dan hal-hal palsu lainnya, jadi kita harus menggunakan is:

any([0 is i for i in unwanted])

... mengevaluasi ke False.

Sekarang gunakan untuk delhal yang tidak diinginkan:

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]

Jika Anda menginginkan kamus baru, alih-alih mengubah metadatadi tempat:

newdict = {k: v for k, v in metadata.items() if not any([v is i for i in unwanted])}

tembakan yang sangat bagus, ini mengatasi banyak masalah sekaligus dan menyelesaikan pertanyaan, terima kasih telah menjelaskannya
jlandercy

Keren! Ini berfungsi untuk contoh ini. Namun, itu tidak berfungsi ketika item dalam kamus adalah[]
jsga

2

Saya membaca semua balasan di utas ini dan beberapa juga merujuk ke utas ini: Hapus dicts kosong di kamus bersarang dengan fungsi rekursif

Saya awalnya menggunakan solusi di sini dan itu berfungsi dengan baik:

Percobaan 1: Terlalu Panas (tidak berkinerja baik atau tidak terbukti masa depan) :

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

Tetapi beberapa masalah kinerja dan kompatibilitas diangkat di dunia Python 2.7:

  1. gunakan isinstancesebagai gantitype
  2. buka gulungan daftar comp ke dalam forlingkaran untuk efisiensi
  3. gunakan aman python3 items bukaniteritems

Percobaan 2: Terlalu Dingin (Kurang Memoisasi) :

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! Ini tidak rekursif dan sama sekali tidak memoizant.

Percobaan 3: Just Right (sejauh ini) :

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
kecuali saya buta, menurut saya upaya 2 dan 3 persis sama ...
luckyguy73

1

Dict dicampur dengan Array

  • Jawaban di Percobaan 3: Just Right (sejauh ini) dari jawaban BlissRage tidak menangani elemen array dengan benar. Saya termasuk tambalan jika ada yang membutuhkannya. Metode ini menangani daftar dengan blok pernyataan if isinstance(v, list):, yang menghapus daftar menggunakan scrub_dict(d)implementasi asli .
    @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

mengagumkan. . . saya telah membuat perubahan ini dalam basis kode tetapi melewatkan komentar Anda _ / _
BlissRage

0

Cara alternatif untuk melakukan ini adalah dengan menggunakan pemahaman kamus. Ini harus kompatibel dengan2.7+

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

0

Berikut adalah opsi jika Anda menggunakan 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

Beberapa Metode yang disebutkan di atas mengabaikan jika ada bilangan bulat dan mengambang dengan nilai 0 & 0,0

Jika seseorang ingin menghindari hal di atas dapat menggunakan kode di bawah ini (menghapus string kosong dan nilai None dari kamus bersarang dan daftar bersarang):

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

"Karena saat ini saya juga menulis aplikasi desktop untuk pekerjaan saya dengan Python, saya menemukan di aplikasi entri data ketika ada banyak entri dan ada beberapa yang tidak wajib sehingga pengguna dapat membiarkannya kosong, untuk tujuan validasi, mudah untuk diambil semua entri dan kemudian buang kunci kosong atau nilai kamus Jadi kode saya di atas menunjukkan bagaimana kita dapat dengan mudah mengeluarkannya, menggunakan pemahaman kamus dan menyimpan elemen nilai kamus yang tidak kosong.Saya menggunakan 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'}

Sebutkan versi python juga apakah itu mendukung versi terbaru?
HaseeB Mir

Jawaban Anda saat ini ditandai karena kualitas rendah mungkin telah dihapus. Pastikan jawaban Anda berisi penjelasan selain kode apa pun.
Tim Stack

@TimStack Tolong rekomendasikan penghapusan untuk jawaban LQ.
10 Rep

@ 10Rep Saya tidak akan merekomendasikan penghapusan untuk jawaban yang mungkin berfungsi sebagai solusi tetapi hanya kekurangan komentar deskriptif. Saya lebih suka memberi tahu pengguna dan mengajari mereka seperti apa jawaban yang lebih baik itu.
Tim Stack

@HasseB Mir Saya menggunakan Python 3.8.3 terbaru
KokoEfraim

-2

Beberapa pembandingan:

1. Pemahaman daftar menciptakan dikt

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. Pemahaman daftar membuat ulang dict menggunakan 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. Loop dan menghapus kunci jika v adalah 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

jadi loop dan delete adalah yang tercepat di 160ns, pemahaman daftar setengah lambat di ~ 375ns dan dengan panggilan ke dict()setengah lambat lagi ~ 680ns.

Membungkus 3 menjadi fungsi membawanya kembali ke sekitar 275ns. Juga bagi saya PyPy sekitar dua kali lebih cepat dari python neet.


Ulangi dan hapus juga dapat memunculkan RunTimeError, karena memodifikasi kamus saat mengulang tampilan tidak valid. docs.python.org/3/library/stdtypes.html s4.10.1
Airsource Ltd

ah man ya ok di python 3 itu benar tetapi tidak di python 2.7 karena item mengembalikan daftar, jadi Anda harus memanggil list(dic.items())di py 3. Dict pemahaman ftw lalu? del masih tampak lebih cepat untuk rasio rendah nilai Null / kosong. Saya kira membangun daftar itu sama buruknya dengan konsumsi memori daripada hanya membuat ulang dict.
Richard Mathie
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.