У меня есть dict и хотел бы удалить все ключи, для которых есть пустые строки значений.
metadata = {u'Composite:PreviewImage': u'(Binary data 101973 bytes)',
u'EXIF:CFAPattern2': u''}
Каков наилучший способ сделать это?
У меня есть dict и хотел бы удалить все ключи, для которых есть пустые строки значений.
metadata = {u'Composite:PreviewImage': u'(Binary data 101973 bytes)',
u'EXIF:CFAPattern2': u''}
Каков наилучший способ сделать это?
dict((k, v) for k, v in metadata.iteritems() if v)
Обратите внимание, что все ваши ключи имеют значения. Это просто, что некоторые из этих значений являются пустой строкой. Нет такой вещи, как ключ в dict без ценности; если бы он не имел значения, это не было бы в dict.
Он может стать еще короче решения BrenBarn (и более читаемый, я думаю)
{k: v for k, v in metadata.items() if v}
Протестировано с помощью Python 2.7.3.
Если вам действительно нужно изменить исходный словарь:
empty_keys = [k for k,v in metadata.iteritems() if not v]
for k in empty_keys:
del metadata[k]
Обратите внимание, что мы должны составить список пустых ключей, потому что мы не можем изменять словарь во время итерации через него (как вы могли заметить). Это дешевле (с точки зрения памяти), чем создание совершенно нового словаря, хотя и не существует много записей с пустыми значениями.
Если вам нужен полнофункциональный, но краткий подход к работе с реальными структурами данных, которые часто вложены и даже могут содержать циклы, я рекомендую просмотреть утилиту переназначения из пакет утилиты 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+. Лучше всего, я написал это для подобных случаев, так что, если вы найдете случай, который он не обрабатывает, вы можете исправить ошибку здесь.
Решение BrenBarn является идеальным (и я могу добавить pythonic). Вот еще одно решение (fp):
from operator import itemgetter
dict(filter(itemgetter(1), metadata.items()))
На основе Ryan solution, если у вас также есть списки и вложенные словари:
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
Если у вас есть вложенный словарь и вы хотите, чтобы он работал даже для пустых подэлементов, вы можете использовать рекурсивный вариант предложения 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
### 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 ='''
{'charlie': 'three', 'echo': False,
'foxy': 'False', 'delta': [],
'bravo': '0', 'alpha': 0
}
'''
Основываясь на ответах 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])}
Для python 3
dict((k, v) for k, v in metadata.items() if v)
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 был примерно в два раза быстрее, чем neet python.