M_Arora
M_Arora

Reputation: 133

Delete keys of a dictionary whose values are empty lists

I have a dictionary where some of the values corressponding to keys are empty lists. I want to delete all such keys

d = {'Receipt total': [], 'Total Amount (AED)': [], 'Grand total': [], 'Net Amount': [], 'Total': ['105.00'], 'Total (AED)': [], 'Total Invoice Amount': [], 'Invoice total': ['105.00'], 'Amount Due': ['0.00']}

Expected output:

d = {'Total': ['105.00'], 'Invoice total': ['105.00'], 'Amount Due': ['0.00']}

I tried:

for key, value in d.items():
    if value is None:
        del d[k]

My code is not working

Upvotes: 0

Views: 1401

Answers (8)

Open AI - Opting Out
Open AI - Opting Out

Reputation: 24133

You have at least three errors in your thinking.

The first is that an empty list is the same as None.

Only None is the same as None.

Secondly, comparing lists, you should use ==. Using is to compare a list means that, even if a list has the same values in it, if it's not the actual same memory address, it won't compare equal.

Also, as you only want to know if the list is empty, you can use the fact that in Python, empty sequences are considered False and non-empty are considered True, so you can use a boolean conditional: if not value: which will be True for an empty list. If values can be something other than lists, then empty strings, zeroes, etc are all also False, so you might want to check more carefully.

Thirdly, you shouldn't modify the size of a container like a dict whilst iterating over it. Either iterate over a copy of it, or create a record of things you want to modify, and then perform the modification afterwards.

The first way, iterating over a copy:

for key, value in list(d.items()):
    if not value:
        del d[key]

The second way, making a set of the keys to remove:

keys_to_remove = {key for key, value in d.items()
                  if not value}

for key in keys_to_remove:
    del d[key]

Upvotes: 3

David
David

Reputation: 8298

What about:

new_d = dict((k,v) for k,v in d.items() if v)

And if you wish to overwrite d just change new_d to d.

There is some risk that might gives the wrong results:

if v when v = [] will evaluate to Falsly, same as 0 so it might remove wrong key. In my answer I didn't address that. One can refer to the following link to understand better: What is Truthy and Falsy? How is it different from True and False?

Upvotes: 0

Kelly Bundy
Kelly Bundy

Reputation: 27588

If the values are all lists, so you can use their truth, you could use itertools.compress.

>>> dict(compress(d.items(), d.values()))
{'Total': ['105.00'], 'Invoice total': ['105.00'], 'Amount Due': ['0.00']}

Upvotes: 2

iron59
iron59

Reputation: 615

The dictionary d you have is of type Dict[str, List[str]]. It is initialised in a way that means none of the values of the keys are None, but rather empty lists. For example:

>>> listOfNothing = []

>>> print(listOfNothing)
[]

>>> print(type(listOfNothing))
<class 'list'>

If you want to check whether the list is empty (whether the value is an empty list), I'd suggest something like this:

for key, value in d.items():
    if len(value) == 0:
        [do something]

However, as correctly pointed out by others, you can't change the size of the dictionary while iterating over it. This can be solved by creating a new dictionary.

Upvotes: -1

Jack Morgan
Jack Morgan

Reputation: 317

You can use dictionary comprehension like this:

d = {'Receipt total': [], 'Total Amount (AED)': [], 'Grand total': [], 'Net Amount': [], 'Total': ['105.00'], 'Total (AED)': [], 'Total Invoice Amount': [], 'Invoice total': ['105.00'], 'Amount Due': ['0.00']}

#use dictionary comprehensiion to create a new list of values where they value is not an empty list
d = {key : value for key, value in d.items() if len(value) != 0}

print(d)

Output : {'Total': ['105.00'], 'Invoice total': ['105.00'], 'Amount Due': ['0.00']}

By using a for loop to delete items of a dictionary, it will raise RuntimeError: dictionary changed size during iteration

Upvotes: 3

costaparas
costaparas

Reputation: 5237

You can use a dictionary comprehension instead:

d = {'Receipt total': [], 'Total Amount (AED)': [], 'Grand total': [], 'Net Amount': [], 'Total': ['105.00'], 'Total (AED)': [], 'Total Invoice Amount': [], 'Invoice total': ['105.00'], 'Amount Due': ['0.00']}
    
d = {k: v for k, v in d.items() if v != []}

print(d)

# d = {'Total': ['105.00'], 'Invoice total': ['105.00'], 'Amount Due': ['0.00']}

You'll probably want to explicitly check whether the value is []. Otherwise, you may remove things that happen to evaluate to False ("falsey"), e.g. 0 values which you may not want. Of course, this point is only relevant if your dict could contain things other than lists as values.

Upvotes: 3

Mehrdad Pedramfar
Mehrdad Pedramfar

Reputation: 11073

Try this:

result = {k:v for k,v in d.items() if v}

Upvotes: 1

Jonas Palačionis
Jonas Palačionis

Reputation: 4842

You can use:

d = {'Receipt total': [], 'Total Amount (AED)': [], 'Grand total': [], 'Net Amount': [], 'Total': ['105.00'], 'Total (AED)': [], 'Total Invoice Amount': [], 'Invoice total': ['105.00'], 'Amount Due': ['0.00']}

res = {}

for key, value in d.items():
    if value:
        res[key] = value

res
# {'Total': ['105.00'], 'Invoice total': ['105.00'], 'Amount Due': ['0.00']}

It is not recommended to delete items from a container during a for loop, better create a new one and add you need than delete from the original what you do not need.

For example:

a = [1,2,2,3]
for item in a:
    if item > 1:
        a.remove(item)
a
# [1, 2]

Leaves the second 2 because once you removed the first 2 you shifted indexes and your for loop has already checked index 1 but now your second 2 is at index 1 and it gets unchecked.

Upvotes: 1

Related Questions