0

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

Kapil
  • 817
  • 2
  • 13
  • 25
M_Arora
  • 113
  • 9

8 Answers8

3

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.

costaparas
  • 5,047
  • 11
  • 16
  • 26
3

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

Jack Morgan
  • 317
  • 1
  • 14
3

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]
Peter Wood
  • 23,859
  • 5
  • 60
  • 99
  • Is there something wrong with changing *values* of a dict while iterating it? Your statement sounds too general to me. – Kelly Bundy Jan 20 '21 at 12:07
  • @KellyBundy sorry, when I said "shouldn't modify a container" I meant the structure of the container, i.e. the amount of values. Certainly, if values in a container are mutable, then you can modify them. Updated. Thanks. – Peter Wood Jan 20 '21 at 12:14
2

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']}
Kelly Bundy
  • 23,480
  • 7
  • 29
  • 65
1

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.

Jonas Palačionis
  • 4,591
  • 4
  • 22
  • 55
1

Try this:

result = {k:v for k,v in d.items() if v}
Mehrdad Pedramfar
  • 10,941
  • 4
  • 38
  • 59
0

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?

David
  • 8,113
  • 2
  • 17
  • 36
  • What if the original dictionary needs modifying? – Peter Wood Jan 20 '21 at 12:05
  • @PeterWood You mean while iterating it? I only suggested a solution that returns the desired output. – David Jan 20 '21 at 12:07
  • The question said they wanted to delete keys. – Peter Wood Jan 20 '21 at 12:09
  • @PeterWood I don't see a significant difference, this will "delete" the unwanted keys and return the desired solution. Most of the solutions are equivalent to what I suggested. – David Jan 20 '21 at 12:11
  • Maybe it would be good to note the risk of bugs in your answer. – Peter Wood Jan 20 '21 at 12:27
  • @PeterWood This code served my purpose. What kind of bugs are you referring to? – M_Arora Jan 20 '21 at 12:35
  • @PeterWood Thanks, I added the problem that might happen. and a link to read more about truthy/falsly. – David Jan 20 '21 at 12:35
  • @DavidS that's not the problem I was talking about, although it's a good point – Peter Wood Jan 20 '21 at 12:36
  • @M_Arora if you found yourself doing this more than a few times, you might think to give it a nice name, reduce duplication and extract it to a function, e.g. `def remove_empty_values(d):` This code wouldn't work. It doesn't modify `d`. – Peter Wood Jan 20 '21 at 12:37
  • @PeterWood can you elaborate on what you refers? – David Jan 20 '21 at 12:38
  • @PeterWood Yes, I'll put it in a function because this ha to be done on multiple dictionaries but the logic worked – M_Arora Jan 20 '21 at 12:40
-1

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.

iron59
  • 555
  • 2
  • 5
  • 12