leaf_soba
leaf_soba

Reputation: 2518

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

description

example

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

cond = {"type":"example","fieldToBreak":["1","2","3"],"fieldInt":1,"fieldFloat":0.1}
[
    {'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

question:

Upvotes: 2

Views: 79

Answers (4)

I'mahdi
I'mahdi

Reputation: 24049

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

Upvotes: 1

alec_djinn
alec_djinn

Reputation: 10799

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.

Upvotes: 1

user10325516
user10325516

Reputation:

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

Upvotes: 2

balderman
balderman

Reputation: 23815

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

Upvotes: 2

Related Questions