Я маю дікт і хотів би видалити всі ключі, для яких є порожні рядки значень.
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}
Зауважте, що всі ваші ключі мають значення. Просто деякі з цих значень є порожнім рядком. Немає такої речі, як ключ у диктаті без значення; якби вона не мала значення, вона не була б у дікті.
.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()
, перший більше не працює в останніх версіях Python.
Рішення BrenBarn є ідеальним (і пітонічним, можу додати). Ось ще одне (fp) рішення:
from operator import itemgetter
dict(filter(itemgetter(1), metadata.items()))
Якщо ви хочете повнофункціональний, але стислий підхід до обробки реальних структур даних, які часто є вкладеними і можуть містити навіть цикли, рекомендую переглянути утиліту перенастроювання з пакету утиліти boltons .
Після 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.
Це pure-Python, тому він працює скрізь і повністю перевірений у Python 2.7 та 3.3+. Найкраще, що я написав це саме для таких випадків, тож якщо ви знайдете справу, з якою вона не справляється, ви можете помилити мене, щоб виправити це тут .
На основі рішення Райана , якщо у вас також є списки та вкладені словники:
Для 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'
}
'''
Для пітона 3
dict((k, v) for k, v in metadata.items() if v)
Спираючись на відповіді patriciasz та nneonneo , і враховуючи можливість того, що ви можете видалити ключі, у яких є лише певні хибні речі (наприклад ''
), але не інші (наприклад 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])}
[]
Я читаю всі відповіді в цій темі, а деякі посилаються також на цю тему: Видаліть порожні дикти в вкладеному словнику з рекурсивною функцією
Я спочатку використовував тут рішення, і воно спрацювало чудово:
Спроба 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
ДОХ! Це не рекурсивно і зовсім не запам'ятовується.
Спроба 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
Якщо хтось хоче уникнути вищезазначеного, може використовувати код нижче (видаляє порожні рядки та значення 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
"Оскільки я також зараз пишу настільний додаток для своєї роботи з 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 у функцію, вона знову знизиться до приблизно 275 секунд. Також для мене PyPy був приблизно вдвічі швидшим, ніж неет-пітон.
list(dic.items())
в py 3. Розуміння розгляду ftw тоді? del все ще здається швидшим для низького співвідношення значень Null / empty. Я думаю, складання цього списку так само погано для споживання пам’яті, ніж просто відтворення диктату.