Удаление ключ-значение по фильтру

Необходимо удалить ключ значение из словаря:

Вот код:

leads = [{'id': 'value2', 'num': 523541, 'status_id': 321, 'group_id': 721},{'id': 'value1', 'num': 123456, 'status_id': 123, 'group_id': 127}]

def removekey(d, key): 
    r = dict.copy(d) 
    del r[key]
    return r 

for i, elem in enumerate(leads):
    for key in leads[i]:
        if key != 'id':
            test = removekey(leads[i], key)

То есть по сути хочу удалить все ключ-значение кроме ключа 'id'. Ошибок нет, но весь словарь остается и не удаляется, хотя в removekey заходит. Подскажите, пожалуйста, почему не удаляет?


Ответы (4 шт):

Автор решения: gil9red

Для удаления значений из словаря используйте метод dict.pop.

Пример:

leads = [{'id': 'value2', 'num': 523541, 'status_id': 321, 'group_id': 721},{'id': 'value1', 'num': 123456, 'status_id': 123, 'group_id': 127}]

for d in leads:
    for key in list(d.keys()):
        if key != 'id':
            d.pop(key)

print(leads)
# [{'id': 'value2'}, {'id': 'value1'}]

Для нескольких ключей проверка была бы например такая:

if key not in ('id', 'num'):
→ Ссылка
Автор решения: insolor

Вы в функции removekey делаете копию словаря, из нее удаляете ключ. Естественно, исходный словарь не изменится. Можно просто менять исходный словарь без создания копии:

leads = [{'id': 'value2', 'num': 523541, 'status_id': 321, 'group_id': 721},{'id': 'value1', 'num': 123456, 'status_id': 123, 'group_id': 127}]

for i, elem in enumerate(leads):
    # Набор ключей собирается в список,
    # чтобы избежать исключения "RuntimeError: dictionary changed size during iteration"
    for key in list(elem):
        if key != 'id':
            del elem[key]

print(leads)

Другой вариант решения - через генератор словаря (dict comprehension):

leads = [{'id': 'value2', 'num': 523541, 'status_id': 321, 'group_id': 721},{'id': 'value1', 'num': 123456, 'status_id': 123, 'group_id': 127}]

for i, elem in enumerate(leads):
    leads[i] = {key: value for key, value in elem.items() if key == 'id'}

print(leads)

Вообще, если нужно значение только по одному ключу, то внутренний цикл не нужен, можно просто брать значение по этому ключу, и записывать в новый словарь:

leads = [{'id': 'value2', 'num': 523541, 'status_id': 321, 'group_id': 721},{'id': 'value1', 'num': 123456, 'status_id': 123, 'group_id': 127}]

for i, elem in enumerate(leads):
    if 'id' in elem:
        leads[i] = dict(id=elem['id'])
    else:
        leads[i] = dict()  # Учитываем случай, когда данного ключа нет в словаре

print(leads)
→ Ссылка
Автор решения: danilfg

Всем спасибо! Решение нашел:

leads = [{'id': 'value2', 'num': 523541, 'status_id': 321, 'group_id': 721},{'id': 'value1', 'num': 123456, 'status_id': 123, 'group_id': 127}]

test = copy.deepcopy(leads)
for i, elem in enumerate(leads):
    for key in leads[i]:
        if key != 'id':
            test[i].pop(key)
→ Ссылка
Автор решения: CrazyElf

Вариант решения в одну строчку :)

leads = [{'id': 'value2', 'num': 523541, 'status_id': 321, 'group_id': 721},{'id': 'value1', 'num': 123456, 'status_id': 123, 'group_id': 127}]
leads = [dict(filter(lambda x: x[0] == 'id', d.items())) for d in leads]

На выходе:

# [{'id': 'value2'}, {'id': 'value1'}]

Как вариант можно ещё вот так:

leads =[{k: v} for d in leads for k,v in d.items() if k == 'id'] 
→ Ссылка