ฉันมีคำสั่งและต้องการลบคีย์ทั้งหมดที่มีสตริงค่าว่าง
metadata = {u'Composite:PreviewImage': u'(Binary data 101973 bytes)',
u'EXIF:CFAPattern2': u''}
วิธีที่ดีที่สุดในการทำคืออะไร?
ฉันมีคำสั่งและต้องการลบคีย์ทั้งหมดที่มีสตริงค่าว่าง
metadata = {u'Composite:PreviewImage': u'(Binary data 101973 bytes)',
u'EXIF:CFAPattern2': u''}
วิธีที่ดีที่สุดในการทำคืออะไร?
คำตอบ:
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
.items()
คุณหมายถึง
{k: v for k, v in metadata.items() if v is not None}
อาจสั้นกว่าโซลูชันของ BrenBarn ด้วยซ้ำ (และฉันคิดว่าอ่านได้มากกว่า)
{k: v for k, v in metadata.items() if v}
ทดสอบด้วย Python 2.7.3
... if v!=None
ดังนี้ {k: v for k, v in metadata.items() if v!=None}
หากคุณต้องการแก้ไขพจนานุกรมต้นฉบับจริงๆ:
empty_keys = [k for k,v in metadata.iteritems() if not v]
for k in empty_keys:
del metadata[k]
โปรดทราบว่าเราต้องสร้างรายการของคีย์ว่างเนื่องจากเราไม่สามารถแก้ไขพจนานุกรมได้ในขณะที่ทำซ้ำผ่าน (ดังที่คุณอาจสังเกตเห็น) สิ่งนี้มีราคาถูกกว่า (ใช้หน่วยความจำ) ได้ดีกว่าการสร้างพจนานุกรมใหม่ล่าสุดเว้นแต่จะมีรายการจำนวนมากที่มีค่าว่างเปล่า
.iteritems()
ด้วย.items()
เป็นครั้งแรกที่ไม่ได้ทำงานอีกต่อไปในรุ่นล่าสุดหลาม
วิธีแก้ปัญหาของ BrenBarnนั้นเหมาะอย่างยิ่ง (และ pythonic ฉันอาจเพิ่ม) นี่คือโซลูชันอื่น (fp) อย่างไรก็ตาม:
from operator import itemgetter
dict(filter(itemgetter(1), metadata.items()))
หากคุณต้องการวิธีการเต็มรูปแบบยังรวบรัดถึงการจัดการที่แท้จริงของโลกโครงสร้างข้อมูลซึ่งมักจะซ้อนกันและยังสามารถมีรอบผมขอแนะนำให้มองหาที่ยูทิลิตี้ remap จากแพคเกจยูทิลิตี้
หลังจากpip install boltons
หรือคัดลอกiterutils.pyไปยังโครงการของคุณให้ทำดังนี้
from boltons.iterutils import remap
drop_falsey = lambda path, key, value: bool(value)
clean = remap(metadata, visit=drop_falsey)
หน้านี้มีตัวอย่างอื่น ๆ อีกมากมายรวมถึงตัวอย่างที่ทำงานกับออบเจ็กต์ขนาดใหญ่กว่ามากจาก API ของ Github
เป็น Python แท้ดังนั้นจึงใช้งานได้ทุกที่และได้รับการทดสอบอย่างสมบูรณ์ใน Python 2.7 และ 3.3+ ดีที่สุดของทั้งหมดที่ผมเขียนมันว่ากรณีเช่นนี้ดังนั้นถ้าคุณพบกรณีที่มันไม่ได้จัดการให้คุณสามารถที่ฉันไม่ชอบที่จะแก้ไขได้ที่นี่
ตามวิธีการแก้ปัญหาของ Ryanหากคุณมีรายการและพจนานุกรมที่ซ้อนกัน:
สำหรับ 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": "" }] }
หากคุณมีพจนานุกรมแบบซ้อนกันและคุณต้องการให้ใช้งานได้แม้กระทั่งสำหรับองค์ประกอบย่อยที่ว่างเปล่าคุณสามารถใช้คำแนะนำของ 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
items()
แทนiteritems()
Python 3
### 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'}
'''
### 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'
}
'''
สำหรับ python 3
dict((k, v) for k, v in metadata.items() if v)
สร้างจากคำตอบจากpatriciaszและnneonneoและพิจารณาถึงความเป็นไปได้ที่คุณอาจต้องการลบคีย์ที่มีเพียงบางสิ่งที่ไม่ถูกต้อง (เช่น''
) แต่ไม่ใช่คีย์อื่น ๆ (เช่น0
) หรือบางทีคุณอาจต้องการรวมสิ่งที่แท้จริงไว้ด้วย (เช่น'SPAM'
) จากนั้นคุณสามารถสร้าง Hitlist ที่เฉพาะเจาะจงมาก:
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])}
[]
ฉันอ่านคำตอบทั้งหมดในเธรดนี้และบางส่วนก็อ้างถึงเธรดนี้ด้วย: ลบคำสั่งที่ว่างเปล่าในพจนานุกรมที่ซ้อนกันด้วยฟังก์ชันเรียกซ้ำ
เดิมทีฉันใช้โซลูชันที่นี่และใช้งานได้ดี:
ความพยายามที่ 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:
isinstance
แทนtype
for
ลูปเพื่อประสิทธิภาพ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
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
อีกทางเลือกหนึ่งที่คุณสามารถทำได้คือการใช้พจนานุกรมเพื่อความเข้าใจ สิ่งนี้ควรเข้ากันได้กับ2.7+
result = {
key: value for key, value in
{"foo": "bar", "lorem": None}.items()
if value
}
นี่คือตัวเลือกหากคุณใช้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
หากมีคนต้องการหลีกเลี่ยงข้างต้นสามารถใช้รหัสด้านล่าง (ลบสตริงว่างและไม่มีค่าออกจากพจนานุกรมที่ซ้อนกันและรายการที่ซ้อนกัน):
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
"ในขณะที่ฉันเขียนแอปพลิเคชันเดสก์ท็อปสำหรับการทำงานกับ 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'}
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
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
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 ประมาณสองเท่า
list(dic.items())
ใน py 3 Dict comp understandion ftw แล้ว? เดลยังดูเหมือนเร็วกว่าสำหรับอัตราส่วนที่ต่ำของค่า Null / ว่าง ฉันเดาว่าการสร้างรายการนั้นไม่ดีต่อการใช้หน่วยความจำมากกว่าแค่การสร้างคำสั่งใหม่