Aleph
Aleph

Reputation: 1373

dictionary of values/lists to list of dictionaries

Given a dictionary containing lists with different lengths but not only, e.g.

d = {foo: 'hello', bar: [0, 2], baz: [1, 2, 3]}

I would like to create a list of all possible combinations where each combination is a dictionary:

l = [{foo: 'hello', bar: 0, baz: 1},
     {foo: 'hello', bar: 0, baz: 2},
     {foo: 'hello', bar: 0, baz: 3},
     {foo: 'hello', bar: 2, baz: 1},
     {foo: 'hello', bar: 2, baz: 2},
     {foo: 'hello', bar: 2, baz: 3}]

The order does not matter here and I don't need to transform back the list into a dictionary.

It is of course possible to perform this operation with Python loops but I'm looking for a solution which is more efficient and/or more elegant.

Upvotes: 1

Views: 57

Answers (3)

Aleph
Aleph

Reputation: 1373

The algorithm below answers the question in a generic way:

def list_of_dictionaries(self, dictionary):
  # values which are not lists are forced to be lists
  for k, v in dictionary.items():
    if type(v) is not list:
        dictionary[k] = [v]

  # list all combinations of values
  combinations = [list(x) for x in dictionary.values() if isinstance(x, list)]
  combinations = list(itertools.product(*combinations))

  # create list of dictionaries
  list_of_dicts = []
  for values in combinations:
    dict_element = {}
    for i, k in enumerate(dictionary.keys()):
        dict_element[k] = values[i]
    list_of_dicts.append(dict_element)

  return list_of_dicts

Upvotes: 0

JonSG
JonSG

Reputation: 13152

I feel like the answer by @DSteman is basically correct and they have given you the tools you need to solve your issue.

I see you are getting stuck on how to finish up given their excellent starting suggestion. So here is a simpler more explicit version that lacks the flexibility they have included but directly produces the result you seek.

If you feel this solves your question I strongly recommend you revisit their answer and consider it as the "solution".

import itertools

data_in = {"foo": "hello", "bar": [0, 2], "baz": [1, 2, 3]}
data_out = [
    {"foo": data_in["foo"], "bar": c[0], "baz": c[1]}
    for c in itertools.product(data_in["bar"], data_in["baz"])
]

print(data_out)

this will produce:

[
    {'foo': 'hello', 'bar': 0, 'baz': 1},
    {'foo': 'hello', 'bar': 0, 'baz': 2},
    {'foo': 'hello', 'bar': 0, 'baz': 3},
    {'foo': 'hello', 'bar': 2, 'baz': 1},
    {'foo': 'hello', 'bar': 2, 'baz': 2},
    {'foo': 'hello', 'bar': 2, 'baz': 3}
]

Upvotes: 2

DSteman
DSteman

Reputation: 1658

Itertools would be great for this. The code could look something like this:

import itertools

d = {'foo': 'hello', 'bar': [0, 2], 'baz': [1, 2, 3]}
a = [list(x) for x in d.values() if isinstance(x, list)]
constant_values = [x for x in d.values() if x not in a]
keys = d.keys()

combinations = list(itertools.product(*a))

Just transform it into a dictionary as the last step using constant_values and keys

Upvotes: 2

Related Questions