pythonic way to break a dict which value is a list to several dict

Viewed 74

description

  • I need to break a dict which value is a list to several dict, keep the other parts.
  • The key I want to break may have different name,but the cond only and always get one value which type is list.

example

  • input
cond = {"type":"image","questionType":["3","4","5"]}

cond = {"type":"example","fieldToBreak":["1","2","3"],"fieldInt":1,"fieldFloat":0.1}
  • output
[
    {'type': 'image', 'questionType': '3'}, 
    {'type': 'image', 'questionType': '4'}, 
    {'type': 'image', 'questionType': '5'}
]

[
    {'type': 'example', 'fieldToBreak': '1', 'fieldInt': 1, 'fieldFloat': 0.1},
    {'type': 'example', 'fieldToBreak': '2', 'fieldInt': 1, 'fieldFloat': 0.1},
    {'type': 'example', 'fieldToBreak': '3', 'fieldInt': 1, 'fieldFloat': 0.1}
]

what I have tried

cond_queue = []
for k,v in cond.items():
    if isinstance(v,list):
        for ele in v:
            cond_copy = cond.copy()
            cond_copy[k] = ele
            cond_queue.append(cond_copy)
        break
  • It works, but I think it is not the best pythonic solution.

question:

  • Any better pythonic solution?
4 Answers

something like the below (the solution is based on the input from the post which I assume represents the general case)

cond = {"type": "image", "questionType": ["3", "4", "5"]}
data = [{"type": "image", "questionType": e} for e in cond['questionType']]
print(data)

output

[{'type': 'image', 'questionType': '3'}, {'type': 'image', 'questionType': '4'}, {'type': 'image', 'questionType': '5'}]

Possible approach utilizing python's built-in functions and standard library. The code should work with any number of keys. It creates all combinations of values' elements in case of multiple lists presented in the original dict. Not sure if this logic a correct one.

import itertools

def dict_to_inflated_list(d):

    ans, keys, vals = list(), list(), list()

    # copy keys and 'listified' values in the same order
    for k, v in d.items():
        keys.append(k)
        vals.append(v if isinstance(v, list) else [v])

    # iterate over all possible combinations of elements of all 'listified' values
    for combination in itertools.product(*vals):
        ans.append({k: v for k, v in zip(keys, combination)})

    return ans

if __name__ == '__main__':

    cond = {'type': 'image', 'questionType': ['3', '4', '5']}
    print(dict_to_inflated_list(cond))

    cond = {'a': 0, 'b': [1, 2], 'c': [10, 20]}
    print(dict_to_inflated_list(cond))

Output:

[{'type': 'image', 'questionType': '3'}, {'type': 'image', 'questionType': '4'}, {'type': 'image', 'questionType': '5'}]
[{'a': 0, 'b': 1, 'c': 10}, {'a': 0, 'b': 1, 'c': 20}, {'a': 0, 'b': 2, 'c': 10}, {'a': 0, 'b': 2, 'c': 20}]

try this:

lens = 0
for index, item in enumerate(cond):
    if isinstance(cond[item], list):
        lens = len(cond[item])
        idx = index
        break
print([{k : v if i!=idx else v[j] for i,(k,v) in enumerate(cond.items()) } for j in range(lens)])

output:

# cond = {"type":"image","questionType":["3","4","5"]}
[{'type': 'image', 'questionType': '3'},
 {'type': 'image', 'questionType': '4'},
 {'type': 'image', 'questionType': '5'}]

# cond = {"type":"example","fieldToBreak":["1","2","3"],"fieldInt":1,"fieldFloat":0.1}
[{'type': 'example', 'fieldToBreak': '1', 'fieldInt': 1, 'fieldFloat': 0.1},
 {'type': 'example', 'fieldToBreak': '2', 'fieldInt': 1, 'fieldFloat': 0.1},
 {'type': 'example', 'fieldToBreak': '3', 'fieldInt': 1, 'fieldFloat': 0.1}]

if dict have another shape:

# cond = {"questionType":["3","4","5"], "type":"image"}
[{'questionType': '3', 'type': 'image'},
 {'questionType': '4', 'type': 'image'},
 {'questionType': '5', 'type': 'image'}]

This little function does the job without any extra argument except the input dictionary

def unpack_dict(d):
    n = [len(v) for k,v in d.items() if type(v) is list][0] #number of items in the list
    r = []
    for i in range(n):
        _d = {}
        for k,v in d.items():
            if type(v) is list:
                _d[k] = v[i]
            else:
                _d[k] = v
        r.append(_d)
    return r
    
    


cond = {"type":"example","fieldToBreak":["1","2","3"],"fieldInt":1,"fieldFloat":0.1}

unpack_dict(cond)

[{'type': 'example', 'fieldToBreak': '1', 'fieldInt': 1, 'fieldFloat': 0.1},
 {'type': 'example', 'fieldToBreak': '2', 'fieldInt': 1, 'fieldFloat': 0.1},
 {'type': 'example', 'fieldToBreak': '3', 'fieldInt': 1, 'fieldFloat': 0.1}]

The function determines how many items (n) there are in the list entry and uses that info to extract the right value to be inserted in the dictionary. Looping over n (for i in range(n):) is used to append the correct number of dictionaries in the final output. That's it. Quite simple to read and understand.

Related