Комбинации из словаря со значениями списка с использованием Python - PullRequest
24 голосов
/ 06 октября 2010

У меня есть следующее входящее значение:

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, но не нашел ничего соответствующего этим требованиям.

Ответы [ 4 ]

32 голосов
/ 06 октября 2010
import itertools as it

varNames = sorted(variants)
combinations = [dict(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'}]

что, вероятно, не совсем то, что вы хотите. Позвольте мне адаптировать это ...

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

Вуаля; -)

7 голосов
/ 06 октября 2010
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 голосов
/ 26 января 2017

Это то, что я использую:

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

Я считаю, что это более читабельно, чем один из вкладышей выше.1008 * Кроме того, он возвращает итератор, такой как itertools.product, поэтому он оставляет за пользователем право создавать экземпляр списка или просто использовать значения по одному.

1 голос
/ 06 октября 2010

Полагаю, вы хотите получить декартово произведение всех ключей?Итак, если бы у вас была другая запись, «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'}]]

Обратите внимание, что это не копирует слова, поэтому все символы {'debug': 'on'} одинаковы.

...