Home > Net >  Create a new list of dict, from a dict with a key that has a list value
Create a new list of dict, from a dict with a key that has a list value

Time:10-18

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}]

  • Related