Combinations from dictionary with list values using Python

后端 未结 4 609
一向
一向 2020-12-08 00:52

I have the following incoming value:

variants = {
  \"debug\" : [\"on\", \"off\"],
  \"locale\" : [\"de_DE\", \"en_US\", \"fr_FR\"],
  ...
}
<
相关标签:
4条回答
  • 2020-12-08 01:30
    import itertools as it
    
    varNames = sorted(variants)
    combinations = [dict(zip(varNames, prod)) for prod in it.product(*(variants[varName] for varName in varNames))]
    

    Hm, this returns:

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

    which is probably not exactly, what you want. Let me adapt it...

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

    returns now:

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

    0 讨论(0)
  • 2020-12-08 01:34
    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'}]]
    
    0 讨论(0)
  • 2020-12-08 01:48

    This is what I use:

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

    which applied to your example gives:

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

    I find this is more readable than the one liners above.

    Also, it returns an iterator like itertools.product so it leaves it up to the user whether to instantiate a list or just consume the values one at a time.

    0 讨论(0)
  • 2020-12-08 01:48

    I assume you want the cartesian product of all the keys? So if you had another entry, "foo", with values [1, 2, 3], then you'd have 18 total entries?

    First, put the values in a list, where each entry is one of the possible variants in that spot. In your case, we want:

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

    To do that:

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

    Next we can use a Cartesian product function to expand it...

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

    Note that this doesn't copy the dicts, so all the {'debug': 'on'} dicts are the same.

    0 讨论(0)
提交回复
热议问题