I have a dict with one of the keys have a value of list
example = {"a":1,"b":[1,2]}
I am trying to unpack example["b"]
and create a list of the same dict with separate example["b"]
value.
output = [{"a":1,"b":1},{"a":1,"b":2}]
I have tried to use a for loop to understand the unpacking and reconstruction of the list of dict but I am seeing a strange behavior - It could be I am missing something simple here...
iter = example.get("b")
new_list = []
for p in iter:
print(f"p is {p}")
tmp_dict = example
tmp_dict["b"] = p
print(tmp_dict)
new_list.append(tmp_dict)
print(new_list)
Output
p is 1
{'a': 1, 'b': 1}
p is 2
{'a': 1, 'b': 2}
[{'a': 1, 'b': 2}, {'a': 1, 'b': 2}]
Why is the first dict in the list gets assigned with example["b"] = 2
although the first print()
shows that p is 1
?
CodePudding user response:
Why is the first dict in the list gets assigned with example["b"] = 2 although the first print() shows that p is 1
Because they are all the same dict: example
. You can see this if you print(id(tmp_dict))
inside the loop, and you'll see that it is always the same object. See What is the id( ) function used for? (it's a python 2 question, but applies to python 3 as well)
If you want to keep your code, you just need to create a new dictionary from example
and assign it to tmp_dict
. See How to copy a dictionary and only edit the copy
tmp_dict = example.copy()
although that basically copies the entire dictionary only to discard the b
key, so you could simply do:
tmp_dict = {"a": example["a"]}
Note, your use of iter
as a variable name is not recommended, since iter
already means something in python
Instead, here's a general approach that works for all cases without hardcoding any keys. Let's first create a temporary dictionary where all values are lists.
temp = {k: v if isinstance(v, list) else [v] for k, v in example.items()}
This allows us to then obtain the list of all the values in our temp
dict as a list of lists.
We want the product of all the values of this temp
dictionary. To do this, we can use the itertools.product
function, and unpack our list of lists to its arguments.
In each iteration, the resulting tuple will have one value per key of the temp
dictionary, so all we need to do is zip
that with our tuple, and create a dict out of those key-value pairs. That gives us our list element!
import itertools
keys = list(temp.keys())
vals = list(temp.values())
result = []
for vals_product in itertools.product(*vals):
d = dict(zip(keys, vals_product))
result.append(d)
Which gives the required result:
[{'a': 1, 'b': 1}, {'a': 1, 'b': 2}]
This even works for an example
with more keys:
example = {'a': 1, 'b': [1, 2], 'c': [1, 2, 3]}
which gives:
[{'a': 1, 'b': 1, 'c': 1},
{'a': 1, 'b': 1, 'c': 2},
{'a': 1, 'b': 1, 'c': 3},
{'a': 1, 'b': 2, 'c': 1},
{'a': 1, 'b': 2, 'c': 2},
{'a': 1, 'b': 2, 'c': 3}]
CodePudding user response:
Just one minor correction: Usage of dict()
example = {"a":1,"b":[1,2]}
iter = example.get("b")
new_list = []
for p in iter:
print(f"p is {p}")
tmp_dict = dict(example)
tmp_dict["b"] = p
print(tmp_dict)
new_list.append(tmp_dict)
print(new_list)
Output is as given below: [{'a': 1, 'b': 1}, {'a': 1, 'b': 2}]