Dict dicampur dengan Array

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.

nneonneo
sumber
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

Jawaban:

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()))
Joel Cornett
sumber
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 .

Mahmoud Hashemi
sumber
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
chachan
sumber
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
Ryan Shea
sumber
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

dreftymac.dll
sumber
5

Untuk python 3

dict((k, v) for k, v in metadata.items() if v)
Hector Oliveros
sumber
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])}
kwinkunks
sumber
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
BlissRage
sumber
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
Marcello de Sales
sumber
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
}
Serguei Fedorov
sumber
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_)
jeschwar.dll
sumber
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
dheeraj .A
sumber
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'}
KokoEfraim
sumber
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.

Richard Mathie
sumber
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