algorithm convertir - Combinaciones de diccionario con valores de lista usando Python




agregar elementos (5)

Tengo el siguiente valor entrante:

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

Quiero procesarlos para obtener el siguiente resultado:

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

Esto debería funcionar con una longitud arbitraria de claves en el diccionario. Jugó con itertools en Python, pero no encontró nada que cumpliera con estos requisitos.


Answers

import itertools as it

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

Hm, esto vuelve:

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

lo que probablemente no es exactamente, lo que quieres. Déjame adaptarlo ...

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

vuelve ahora:

[[{'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à ;-)


Supongo que quieres el producto cartesiano de todas las llaves. Entonces, si tuvieras otra entrada, "foo", con valores [1, 2, 3], ¿tendrías 18 entradas en total?

Primero, coloque los valores en una lista, donde cada entrada sea una de las posibles variantes en ese lugar. En su caso, queremos:

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

Para hacer eso:

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

A continuación podemos usar una función de producto cartesiano para expandirla ...

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

Tenga en cuenta que esto no copia los dicts, por lo que todos los dicts {'debug': 'on'} son los mismos.


Esto es lo que uso:

from itertools import product

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

el cual aplicado a tu ejemplo da:

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

Me parece que esto es más legible que las líneas anteriores.

Además, devuelve un iterador como itertools.product por lo que se lo deja al usuario si instanciar una lista o simplemente consumir los valores uno a la vez.


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

La versión tl; dr

La type(obj)función le da el tipo de un objeto.

El type()de una clase es su metaclase .

Para utilizar una metaclase:

class Foo(object):
    __metaclass__ = MyMetaClass




python algorithm list dictionary combinations