Комбинации словаря со значениями в списке с использованием Python
У меня есть следующее входящее значение:
variants = {
"debug" : ["on", "off"],
"locale" : ["de_DE", "en_US", "fr_FR"],
...
}
Я хочу обработать их, чтобы получить следующий результат:
combinations = [
[{"debug":"on"},{"locale":"de_DE"}],
[{"debug":"on"},{"locale":"en_US"}],
[{"debug":"on"},{"locale":"fr_FR"}],
[{"debug":"off"},{"locale":"de_DE"}],
[{"debug":"off"},{"locale":"en_US"}],
[{"debug":"off"},{"locale":"fr_FR"}]
]
Это должно работать с произвольной длиной ключей в словаре. Играл с itertools в Python, но не нашел ничего соответствующего этим требованиям.
Ответы
Ответ 1
import itertools as it
varNames = sorted(variants)
combinations = [dict(zip(varNames, prod)) for prod in it.product(*(variants[varName] for varName in varNames))]
Hm, это возвращает:
[{'debug': 'on', 'locale': 'de_DE'},
{'debug': 'on', 'locale': 'en_US'},
{'debug': 'on', 'locale': 'fr_FR'},
{'debug': 'off', 'locale': 'de_DE'},
{'debug': 'off', 'locale': 'en_US'},
{'debug': 'off', 'locale': 'fr_FR'}]
который, вероятно, не совсем то, что вы хотите. Позвольте мне приспособить его...
combinations = [ [ {varName: val} for varName, val in zip(varNames, prod) ] for prod in it.product(*(variants[varName] for varName in varNames))]
теперь возвращается:
[[{'debug': 'on'}, {'locale': 'de_DE'}],
[{'debug': 'on'}, {'locale': 'en_US'}],
[{'debug': 'on'}, {'locale': 'fr_FR'}],
[{'debug': 'off'}, {'locale': 'de_DE'}],
[{'debug': 'off'}, {'locale': 'en_US'}],
[{'debug': 'off'}, {'locale': 'fr_FR'}]]
Voilà; -)
Ответ 2
combinations = [[{key: value} for (key, value) in zip(variants, values)]
for values in itertools.product(*variants.values())]
[[{'debug': 'on'}, {'locale': 'de_DE'}],
[{'debug': 'on'}, {'locale': 'en_US'}],
[{'debug': 'on'}, {'locale': 'fr_FR'}],
[{'debug': 'off'}, {'locale': 'de_DE'}],
[{'debug': 'off'}, {'locale': 'en_US'}],
[{'debug': 'off'}, {'locale': 'fr_FR'}]]
Ответ 3
Это то, что я использую:
from itertools import product
def dictproduct(dct):
for t in product(*dct.itervalues()):
yield dict(zip(dct.iterkeys(), t))
который применяется к вашему примеру, дает:
>>> list(dictproduct({"debug":["on", "off"], "locale":["de_DE", "en_US", "fr_FR"]}))
[{'debug': 'on', 'locale': 'de_DE'},
{'debug': 'on', 'locale': 'en_US'},
{'debug': 'on', 'locale': 'fr_FR'},
{'debug': 'off', 'locale': 'de_DE'},
{'debug': 'off', 'locale': 'en_US'},
{'debug': 'off', 'locale': 'fr_FR'}]
Я считаю, что это более читаемо, чем те, которые были выше.
Кроме того, он возвращает итератор, например itertools.product
, поэтому он оставляет его пользователю, создавать ли экземпляр списка или просто использовать значения по одному за раз.
Ответ 4
Я предполагаю, что вы хотите декартово произведение всех ключей? Итак, если у вас была другая запись "foo" со значениями [1, 2, 3], то у вас было бы 18 общих записей?
Сначала поместите значения в список, где каждая запись является одним из возможных вариантов в этом месте. В вашем случае мы хотим:
[[{'debug': 'on'}, {'debug': 'off'}], [{'locale': 'de_DE'}, {'locale': 'en_US'}, {'locale': 'fr_FR'}]]
Для этого:
>>> stuff = []
>>> for k,v in variants.items():
blah = []
for i in v:
blah.append({k:i})
stuff.append(blah)
>>> stuff
[[{'debug': 'on'}, {'debug': 'off'}], [{'locale': 'de_DE'}, {'locale': 'en_US'}, {'locale': 'fr_FR'}]]
Далее мы можем использовать декартову функцию произведения, чтобы ее расширить...
>>> def cartesian_product(lists, previous_elements = []):
if len(lists) == 1:
for elem in lists[0]:
yield previous_elements + [elem, ]
else:
for elem in lists[0]:
for x in cartesian_product(lists[1:], previous_elements + [elem, ]):
yield x
>>> list(cartesian_product(stuff))
[[{'debug': 'on'}, {'locale': 'de_DE'}], [{'debug': 'on'}, {'locale': 'en_US'}], [{'debug': 'on'}, {'locale': 'fr_FR'}], [{'debug': 'off'}, {'locale': 'de_DE'}], [{'debug': 'off'}, {'locale': 'en_US'}], [{'debug': 'off'}, {'locale': 'fr_FR'}]]
Обратите внимание, что это не копирует dicts, поэтому все теги {'debug': 'on'}
те же.