In a code that evaluates a web response, I would like to zip elements from several lists. However, the elements of the iterators are dict
's.
Therefore, I would like to fill up the missing values also with dict
's, but each generated element should have it's own dict
instance.
The following code groups elements from each list by itertools.zip_longest
.
As long as there is a non mutable fillvalue
specified, there is no problem.
import collections
import itertools
l1 = [{"a": 100}, {"b": 200}, {"c": 300}]
l2 = [{"d": 400}]
ll = list(itertools.zip_longest(l1, l2, fillvalue=0))
print(ll)
-> [({'a': 100}, {'d': 400}), ({'b': 200}, 0), ({'c': 300}, 0)]
Now, when a mutable fillvalue
is specified, all the fillvalue
's share the same instance and so changing one, changes all:
import collections
import itertools
l1 = [{"a": 100}, {"b": 200}, {"c": 300}]
l2 = [{"d": 400}]
ll = list(itertools.zip_longest(l1, l2, fillvalue=dict()))
ll[1][1]["x"] = 150
print(ll)
-> [({'a': 100}, {'d': 400}), ({'b': 200}, {'x': 150}), ({'c': 300}, {'x': 150})]
To prevent that all the dicts share the same instance I used copy.deepcopy
:
import collections
import copy
import itertools
l1 = [{"a": 100}, {"b": 200}, {"c": 300}]
l2 = [{"d": 400}]
ll = list(itertools.zip_longest(l1, l2, fillvalue=copy.deepcopy(dict())))
ll[1][1]["x"] = 150
print(ll)
-> [({'a': 100}, {'d': 400}), ({'b': 200}, {'x': 150}), ({'c': 300}, {'x': 150})]
As a result, still all dict
's from the fillvalue
share the same instance.
I would like to add that ll = [item or dict() for item in itertools.zip_longest(l1, l2)]
works neither, assuming a fillvalue of None
.
So, how can I make each fillvalue
unique?
You can use a sentinel value and wrap zip_longest
in another pipeline.
For example:
sentinel = object()
l = list(tuple({} if x is sentinel else x for x in items) for items in zip_longest(l1, l2, fillvalue=sentinel))
For this example, you can probably use None
rather than a custom sentinel
object.