I have a situation with some parallel lists that need to be filtered based on the values in one of the lists. Sometimes I write something like this to filter them:
lista = [1, 2, 3]
listb = [7, 8, 9]
filtered_a, filtered_b = zip(*[(a, b) for (a, b) in zip(lista, listb) if a < 3])
This gives filtered_a == (1, 2)
and filtered_b == (7, 8)
However, changing the final condition from a < 3
to a < 0
causes an exception to be raised:
Traceback (most recent call last):
...
ValueError: need more than 0 values to unpack
I know why this is happening: the list comprehension is empty, so it's like calling zip(*[])
, which is like zip()
, which just returns an empty list which cannot be unpacked into separate filtered_a and filtered_b iterables.
Is there a better (shorter, simpler, more pythonic) filtering function that handles the empty case? In the empty case, I would expect filtered_a, and filtered_b to be empty iterables so any following code could remain unchanged.
Unpacking a tuple means splitting the tuple's elements into individual variables. For example: x, y = (1, 2) Code language: Python (python)
Tuple packing refers to assigning multiple values into a tuple. Tuple unpacking refers to assigning a tuple into multiple variables.
Unpacking Tuples When we put tuples on both sides of an assignment operator, a tuple unpacking operation takes place. The values on the right are assigned to the variables on the left according to their relative position in each tuple . As you can see in the above example, a will be 1 , b will be 2 , and c will be 3 .
You could simply short-circuit with the default values:
filtered_a, filtered_b = zip(*[(a, b) for a, b in zip(lista, listb) if a < 0]) or ([], [])
print(filtered_b, filtered_a)
# [] []
For Python 3, you'll need to call list
on the iterator returned by zip
so the first operand can be evaluated as an empty list (not an iterator), else the default value is never reached even when the iterator is potentially empty since bool(iter([]))
is True
.
I'd probably do something like:
lista = [1, 2, 3]
listb = [7, 8, 9]
filtered_abs = ((a, b) for (a, b) in zip(lista, listb) if a < 3])
for a, b in filtered_abs:
do_thing(a, b)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With