Эффективный способ удаления ключей с пустыми строками из dict

У меня есть dict и хотел бы удалить все ключи, для которых есть пустые строки значений.

metadata = {u'Composite:PreviewImage': u'(Binary data 101973 bytes)',
            u'EXIF:CFAPattern2': u''}

Каков наилучший способ сделать это?

Ответ 1

dict((k, v) for k, v in metadata.iteritems() if v)

Обратите внимание, что все ваши ключи имеют значения. Это просто, что некоторые из этих значений являются пустой строкой. Нет такой вещи, как ключ в dict без ценности; если бы он не имел значения, это не было бы в dict.

Ответ 2

Он может стать еще короче решения BrenBarn (и более читаемый, я думаю)

{k: v for k, v in metadata.items() if v}

Протестировано с помощью Python 2.7.3.

Ответ 3

Если вам действительно нужно изменить исходный словарь:

empty_keys = [k for k,v in metadata.iteritems() if not v]
for k in empty_keys:
    del metadata[k]

Обратите внимание, что мы должны составить список пустых ключей, потому что мы не можем изменять словарь во время итерации через него (как вы могли заметить). Это дешевле (с точки зрения памяти), чем создание совершенно нового словаря, хотя и не существует много записей с пустыми значениями.

Ответ 4

Если вам нужен полнофункциональный, но краткий подход к работе с реальными структурами данных, которые часто вложены и даже могут содержать циклы, я рекомендую просмотреть утилиту переназначения из пакет утилиты 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+. Лучше всего, я написал это для подобных случаев, так что, если вы найдете случай, который он не обрабатывает, вы можете исправить ошибку здесь.

Ответ 5

Решение BrenBarn является идеальным (и я могу добавить pythonic). Вот еще одно решение (fp):

from operator import itemgetter
dict(filter(itemgetter(1), metadata.items()))

Ответ 6

На основе 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

Ответ 7

Если у вас есть вложенный словарь и вы хотите, чтобы он работал даже для пустых подэлементов, вы можете использовать рекурсивный вариант предложения 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

Ответ 8

Быстрый ответ (TL; DR)

Example01

### 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'}
'''

Подробный ответ

Проблема

  • Контекст: Python 2.x
  • Сценарий: Разработчик хочет изменить словарь, чтобы исключить пустые значения
    • aka удалить пустые значения из словаря
    • aka удалить ключи с пустыми значениями
    • aka filter dictionary для непустых значений по каждой паре значений ключа

Решение

  • example01 использовать синтаксис синтаксиса python с простым условным для удаления "пустых" значений

Ловушки

  • example01 работает только с копией исходного словаря (не изменяется на месте)
  • example01 может давать неожиданные результаты в зависимости от того, что означает средство разработчика,
    • Значит ли разработчик поддерживать значения ложные?
    • Если значения в словаре не гарантируются для строк, разработчик может неожиданно потерять данные.
    • result01 показывает, что из исходного набора сохранялись только три пары ключ-значение

Альтернативный пример

  • example02 помогает справиться с потенциальными ловушками
  • Подходом является использование более точного определения "пустого" путем изменения условного выражения.
  • Здесь мы хотим только отфильтровать значения, которые оцениваются в пустые строки.
  • Здесь мы также используем .strip(), чтобы отфильтровать значения, которые состоят только из пробелов.

Example02

### 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
  }
'''

См. также

Ответ 9

Основываясь на ответах 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])}

Ответ 10

Для python 3

dict((k, v) for k, v in metadata.items() if v)

Ответ 11

Некоторые бенчмаркинга:

1. Пояснение списка воссоздать dict

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. Учет списка воссоздает dict, используя 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 и delete, если v - 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

Таким образом, цикл и удаление являются самыми быстрыми на 160ns, понимание списка наполовину медленнее на ~ 375ns и с вызовом dict() в два раза меньше, чем медленнее - 680ns.

Обертка 3 в функцию возвращает ее обратно примерно в 275 нс. Также для меня PyPy был примерно в два раза быстрее, чем neet python.