Ответы:
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 без значения не бывает ключа; если бы у него не было значения, его бы не было в 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()
, первый больше не работает в последних версиях 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.
Это чистый 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'
}
'''
Для Python 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
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.
Если кто-то хочет избежать вышеуказанного, можно использовать приведенный ниже код (удаляет пустые строки и значения 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
поэтому цикл и удаление являются самыми быстрыми при 160 нс, понимание списка вдвое медленнее при ~ 375 нс, а с вызовом снова dict()
вдвое медленнее ~ 680 нс.
Обертывание 3 в функцию снова снижает его примерно до 275 нс. Также для меня PyPy был примерно в два раза быстрее, чем neet python.
list(dic.items())
py 3. Тогда понимание dict ftw? del по-прежнему кажется быстрее при низком соотношении значений Null / empty. Я предполагаю, что создание этого списка так же плохо для потребления памяти, чем просто воссоздание dict.