2010-10-06 15 views
23

Mam następującą wartość przychodzące:Kombinacje ze słownika z wartościami listy za pomocą Pythona

variants = { 
    "debug" : ["on", "off"], 
    "locale" : ["de_DE", "en_US", "fr_FR"], 
    ... 
} 

Chcę przetwarzać je tak, pojawia się następujący wynik:

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

to powinno działać z dowolnej długości kluczy w słowniku. Zagrał w itertools w Pythonie, ale nie znalazł niczego spełniającego te wymagania.

+1

czy jesteś pewien, że nie chcesz mieć listy dyktatur dwuelementowych? – SilentGhost

Odpowiedz

31
import itertools as it 

varNames = sorted(variants) 
combinations = [dict(zip(varNames, prod)) for prod in it.product(*(variants[varName] for varName in varNames))] 

Hm, to zwraca:

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

który prawdopodobnie nie dokładnie, co chcesz. Pozwól mi ją dostosować ...

combinations = [ [ {varName: val} for varName, val in zip(varNames, prod) ] for prod in it.product(*(variants[varName] for varName in varNames))] 

powraca teraz:

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

voila ;-)

+0

Wow, to było szybkie i dokładnie to, czego szukałem. Dzięki. Czasami pytanie jest o wiele szybsze niż samodzielne samodzielne wykonywanie wielu godzin. –

+2

W rzeczywistości twoje pierwsze rozwiązanie jest lepsze dla mnie. Byłem tylko błędnym przekonaniem, że poprosiłem o ten, który zbudowałeś w drugim :) –

1

zakładam chcesz iloczyn kartezjański wszystkich kluczy? Więc jeśli miałbyś inny wpis, "foo", z wartościami [1, 2, 3], to miałbyś 18 całkowitych wpisów?

Najpierw umieść wartości na liście, gdzie każdy wpis jest jednym z możliwych wariantów w tym miejscu. W twoim przypadku, chcemy:

[[{'debug': 'on'}, {'debug': 'off'}], [{'locale': 'de_DE'}, {'locale': 'en_US'}, {'locale': 'fr_FR'}]] 

Aby to zrobić:

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

Następny możemy wykorzystać funkcję iloczyn kartezjański, aby go rozwinąć ...

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

Należy pamiętać, że to nie robi kopiują dykta, więc wszystkie dyktatury {'debug': 'on'} są takie same.

+0

heh zabawne, jak cały ten kod jest odpowiednikiem wcześniejszych linerów. miło wiedzieć, że cartesian_product jest wbudowany, nigdy nie wiedziałem! – Claudiu

+0

itertools.product (oraz kombinacje i permutacje) był doskonałym dodatkiem do itertools w python 2.6. Jednak nie podoba mi się to imię.Powinno być, jak piszesz, "cartesian_product", produkt powinien być produktem elementów w iteracji: reduce (operator.mul, it) – tokland

+0

, a następnie masz 18 wpisów ogółem? Tak :) Nie było nowej nazwy tego, co chciałbym mieć. Dzięki za wskazówkę. –

7
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

To co mam użyć:

from itertools import product 

def dictproduct(dct): 
    for t in product(*dct.itervalues()): 
     yield dict(zip(dct.iterkeys(), t)) 

który zastosowano do przykładu daje:

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

znajdę ten jest bardziej czytelny niż jednej wkładki powyżej.

Ponadto zwraca iterator taki jak itertools.product, więc pozostawia to użytkownikowi pytanie, czy utworzyć instancję listy, czy po prostu zużywać wartości pojedynczo.

Powiązane problemy